Merge branch 'main' of gitea.suda.codes:giteauser/ghostfolio-mirror

This commit is contained in:
ksyasuda 2024-08-31 09:46:44 -07:00
commit 3fa32b2ae9
14 changed files with 502 additions and 383 deletions

View File

@ -5,7 +5,7 @@ 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).
## 2.106.0-beta.3 - 2024-08-28
## 2.106.0-beta.5 - 2024-08-31
### Added
@ -14,9 +14,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Reworked the portfolio calculator
- Improved the caching of the portfolio snapshot in the portfolio calculator by returning cached data and recalculating in the background when it expires
- Exposed the log levels as an environment variable (`LOG_LEVELS`)
- Exposed the maximum of chart data items as an environment variable (`MAX_CHART_ITEMS`)
- Changed the data format of the environment variable `CACHE_QUOTES_TTL` from seconds to milliseconds
- Changed the data format of the environment variable `CACHE_TTL` from seconds to milliseconds
- Removed the environment variable `MAX_ITEM_IN_CACHE`
- Improved the language localization for Polish (`pl`)
- Migrated from `cache-manager-redis-store` to `cache-manager-redis-yet`
- Upgraded `cache-manager` from version `3.4.3` to `5.7.6`
- Upgraded `prisma` from version `5.18.0` to `5.19.0`
### Fixed

View File

@ -443,7 +443,7 @@ export class BenchmarkService {
benchmarks,
expiration: expiration.getTime()
}),
ms('12 hours') / 1000
0
);
}

View File

@ -1,6 +1,7 @@
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface';
import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service';
import { PortfolioOrder } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-order.interface';
import { PortfolioSnapshotValue } from '@ghostfolio/api/app/portfolio/interfaces/snapshot-value.interface';
import { TransactionPointSymbol } from '@ghostfolio/api/app/portfolio/interfaces/transaction-point-symbol.interface';
import { TransactionPoint } from '@ghostfolio/api/app/portfolio/interfaces/transaction-point.interface';
import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service';
@ -32,6 +33,7 @@ import { Logger } from '@nestjs/common';
import { Big } from 'big.js';
import { plainToClass } from 'class-transformer';
import {
addMilliseconds,
differenceInDays,
eachDayOfInterval,
endOfDay,
@ -863,6 +865,29 @@ export abstract class PortfolioCalculator {
return chartDateMap;
}
private async computeAndCacheSnapshot() {
const snapshot = await this.computeSnapshot();
const expiration = addMilliseconds(
new Date(),
this.configurationService.get('CACHE_QUOTES_TTL')
);
this.redisCacheService.set(
this.redisCacheService.getPortfolioSnapshotKey({
filters: this.filters,
userId: this.userId
}),
JSON.stringify(<PortfolioSnapshotValue>(<unknown>{
expiration: expiration.getTime(),
portfolioSnapshot: snapshot
})),
0
);
return snapshot;
}
@LogPerformance
private computeTransactionPoints() {
this.transactionPoints = [];
@ -1006,19 +1031,33 @@ export abstract class PortfolioCalculator {
private async initialize() {
const startTimeTotal = performance.now();
const cachedSnapshot = await this.redisCacheService.get(
this.redisCacheService.getPortfolioSnapshotKey({
filters: this.filters,
userId: this.userId
})
);
let cachedPortfolioSnapshot: PortfolioSnapshot;
let isCachedPortfolioSnapshotExpired = false;
if (cachedSnapshot) {
this.snapshot = plainToClass(
PortfolioSnapshot,
JSON.parse(cachedSnapshot)
try {
const cachedPortfolioSnapshotValue = await this.redisCacheService.get(
this.redisCacheService.getPortfolioSnapshotKey({
filters: this.filters,
userId: this.userId
})
);
const { expiration, portfolioSnapshot }: PortfolioSnapshotValue =
JSON.parse(cachedPortfolioSnapshotValue);
cachedPortfolioSnapshot = plainToClass(
PortfolioSnapshot,
portfolioSnapshot
);
if (isAfter(new Date(), new Date(expiration))) {
isCachedPortfolioSnapshotExpired = true;
}
} catch {}
if (cachedPortfolioSnapshot) {
this.snapshot = cachedPortfolioSnapshot;
Logger.debug(
`Fetched portfolio snapshot from cache in ${(
(performance.now() - startTimeTotal) /
@ -1026,17 +1065,14 @@ export abstract class PortfolioCalculator {
).toFixed(3)} seconds`,
'PortfolioCalculator'
);
} else {
this.snapshot = await this.computeSnapshot();
this.redisCacheService.set(
this.redisCacheService.getPortfolioSnapshotKey({
filters: this.filters,
userId: this.userId
}),
JSON.stringify(this.snapshot),
this.configurationService.get('CACHE_QUOTES_TTL')
);
if (isCachedPortfolioSnapshotExpired) {
// Compute in the background
this.computeAndCacheSnapshot();
}
} else {
// Wait for computation
this.snapshot = await this.computeAndCacheSnapshot();
}
}
}

View File

@ -0,0 +1,4 @@
export interface PortfolioSnapshotValue {
expiration: number;
portfolioSnapshot: string;
}

View File

@ -1,7 +0,0 @@
import { Cache } from 'cache-manager';
import type { RedisStore } from './redis-store.interface';
export interface RedisCache extends Cache {
store: RedisStore;
}

View File

@ -1,8 +0,0 @@
import { Store } from 'cache-manager';
import { createClient } from 'redis';
export interface RedisStore extends Store {
getClient: () => ReturnType<typeof createClient>;
isCacheableValue: (value: any) => boolean;
name: 'redis';
}

View File

@ -3,31 +3,31 @@ import { ConfigurationService } from '@ghostfolio/api/services/configuration/con
import { CacheModule } from '@nestjs/cache-manager';
import { Module } from '@nestjs/common';
import * as redisStore from 'cache-manager-redis-store';
import { redisStore } from 'cache-manager-redis-yet';
import type { RedisClientOptions } from 'redis';
import { RedisCacheService } from './redis-cache.service';
@Module({
exports: [RedisCacheService],
imports: [
CacheModule.registerAsync({
CacheModule.registerAsync<RedisClientOptions>({
imports: [ConfigurationModule],
inject: [ConfigurationService],
useFactory: async (configurationService: ConfigurationService) => {
const redisPassword = encodeURIComponent(
configurationService.get('REDIS_PASSWORD')
);
return <RedisClientOptions>{
db: configurationService.get('REDIS_DB'),
host: configurationService.get('REDIS_HOST'),
max: configurationService.get('MAX_ITEM_IN_CACHE'),
password: configurationService.get('REDIS_PASSWORD'),
port: configurationService.get('REDIS_PORT'),
store: redisStore,
ttl: configurationService.get('CACHE_TTL')
ttl: configurationService.get('CACHE_TTL'),
url: `redis://${redisPassword ? `:${redisPassword}` : ''}@${configurationService.get('REDIS_HOST')}:${configurationService.get('REDIS_PORT')}/${configurationService.get('REDIS_DB')}`
};
}
}),
ConfigurationModule
],
providers: [RedisCacheService],
exports: [RedisCacheService]
providers: [RedisCacheService]
})
export class RedisCacheModule {}

View File

@ -1,4 +1,4 @@
import { RedisCacheService } from './redis-cache.service';
import { Milliseconds } from 'cache-manager';
export const RedisCacheServiceMock = {
get: (key: string): Promise<string> => {
@ -7,7 +7,7 @@ export const RedisCacheServiceMock = {
getPortfolioSnapshotKey: (userId: string): string => {
return `portfolio-snapshot-${userId}`;
},
set: (key: string, value: string, ttlInSeconds?: number): Promise<string> => {
set: (key: string, value: string, ttl?: Milliseconds): Promise<string> => {
return Promise.resolve(value);
}
};

View File

@ -4,17 +4,17 @@ import { AssetProfileIdentifier, Filter } from '@ghostfolio/common/interfaces';
import { CACHE_MANAGER } from '@nestjs/cache-manager';
import { Inject, Injectable, Logger } from '@nestjs/common';
import { Milliseconds } from 'cache-manager';
import { RedisCache } from 'cache-manager-redis-yet';
import { createHash } from 'crypto';
import type { RedisCache } from './interfaces/redis-cache.interface';
@Injectable()
export class RedisCacheService {
public constructor(
@Inject(CACHE_MANAGER) private readonly cache: RedisCache,
private readonly configurationService: ConfigurationService
) {
const client = cache.store.getClient();
const client = cache.store.client;
client.on('error', (error) => {
Logger.error(error, 'RedisCacheService');
@ -81,11 +81,11 @@ export class RedisCacheService {
return this.cache.reset();
}
public async set(key: string, value: string, ttlInSeconds?: number) {
public async set(key: string, value: string, ttl?: Milliseconds) {
return this.cache.set(
key,
value,
ttlInSeconds ?? this.configurationService.get('CACHE_TTL')
ttl ?? this.configurationService.get('CACHE_TTL')
);
}
}

View File

@ -21,7 +21,7 @@ export class ConfigurationService {
API_KEY_FINANCIAL_MODELING_PREP: str({ default: '' }),
API_KEY_OPEN_FIGI: str({ default: '' }),
API_KEY_RAPID_API: str({ default: '' }),
CACHE_QUOTES_TTL: num({ default: ms('1 minute') / 1000 }),
CACHE_QUOTES_TTL: num({ default: ms('1 minute') }),
CACHE_TTL: num({ default: 1 }),
DATA_SOURCE_EXCHANGE_RATES: str({ default: DataSource.YAHOO }),
DATA_SOURCE_IMPORT: str({ default: DataSource.YAHOO }),
@ -43,7 +43,6 @@ export class ConfigurationService {
JWT_SECRET_KEY: str({}),
MAX_ACTIVITIES_TO_IMPORT: num({ default: Number.MAX_SAFE_INTEGER }),
MAX_CHART_ITEMS: num({ default: 365 }),
MAX_ITEM_IN_CACHE: num({ default: 9999 }),
PORT: port({ default: 3333 }),
REDIS_DB: num({ default: 0 }),
REDIS_HOST: str({ default: 'localhost' }),

View File

@ -29,7 +29,6 @@ export interface Environment extends CleanedEnvAccessors {
JWT_SECRET_KEY: string;
MAX_ACTIVITIES_TO_IMPORT: number;
MAX_CHART_ITEMS: number;
MAX_ITEM_IN_CACHE: number;
PORT: number;
REDIS_DB: number;
REDIS_HOST: string;

File diff suppressed because it is too large Load Diff

295
package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "ghostfolio",
"version": "2.103.0",
"version": "2.106.0-beta.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "ghostfolio",
"version": "2.103.0",
"version": "2.106.0-beta.3",
"hasInstallScript": true,
"license": "AGPL-3.0",
"dependencies": {
@ -30,7 +30,7 @@
"@dinero.js/currencies": "2.0.0-alpha.8",
"@internationalized/number": "3.5.2",
"@nestjs/bull": "10.0.1",
"@nestjs/cache-manager": "2.1.0",
"@nestjs/cache-manager": "2.2.2",
"@nestjs/common": "10.1.3",
"@nestjs/config": "3.0.0",
"@nestjs/core": "10.1.3",
@ -40,7 +40,7 @@
"@nestjs/platform-express": "10.1.3",
"@nestjs/schedule": "3.0.2",
"@nestjs/serve-static": "4.0.0",
"@prisma/client": "5.18.0",
"@prisma/client": "5.19.0",
"@simplewebauthn/browser": "9.0.1",
"@simplewebauthn/server": "9.0.3",
"@stripe/stripe-js": "3.5.0",
@ -49,8 +49,8 @@
"body-parser": "1.20.2",
"bootstrap": "4.6.0",
"bull": "4.10.4",
"cache-manager": "3.4.3",
"cache-manager-redis-store": "2.0.0",
"cache-manager": "5.7.6",
"cache-manager-redis-yet": "5.1.4",
"chart.js": "4.2.0",
"chartjs-adapter-date-fns": "3.0.0",
"chartjs-chart-treemap": "2.3.1",
@ -84,7 +84,7 @@
"passport": "0.7.0",
"passport-google-oauth20": "2.0.0",
"passport-jwt": "4.0.1",
"prisma": "5.18.0",
"prisma": "5.19.0",
"reflect-metadata": "0.1.13",
"rxjs": "7.5.6",
"stripe": "15.11.0",
@ -126,7 +126,7 @@
"@trivago/prettier-plugin-sort-imports": "4.3.0",
"@types/big.js": "6.2.2",
"@types/body-parser": "1.19.5",
"@types/cache-manager": "3.4.2",
"@types/cache-manager": "4.0.6",
"@types/color": "3.0.6",
"@types/google-spreadsheet": "3.1.5",
"@types/jest": "29.4.4",
@ -6800,14 +6800,14 @@
"integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ=="
},
"node_modules/@nestjs/cache-manager": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@nestjs/cache-manager/-/cache-manager-2.1.0.tgz",
"integrity": "sha512-9kep3a8Mq5cMuXN/anGhSYc0P48CRBXk5wyJJRBFxhNkCH8AIzZF4CASGVDIEMmm3OjVcEUHojjyJwCODS17Qw==",
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/@nestjs/cache-manager/-/cache-manager-2.2.2.tgz",
"integrity": "sha512-+n7rpU1QABeW2WV17Dl1vZCG3vWjJU1MaamWgZvbGxYE9EeCM0lVLfw3z7acgDTNwOy+K68xuQPoIMxD0bhjlA==",
"license": "MIT",
"peerDependencies": {
"@nestjs/common": "^9.0.0 || ^10.0.0",
"@nestjs/core": "^9.0.0 || ^10.0.0",
"cache-manager": "<=5",
"reflect-metadata": "^0.1.12",
"rxjs": "^7.0.0"
}
},
@ -9646,9 +9646,9 @@
"dev": true
},
"node_modules/@prisma/client": {
"version": "5.18.0",
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.18.0.tgz",
"integrity": "sha512-BWivkLh+af1kqC89zCJYkHsRcyWsM8/JHpsDMM76DjP3ZdEquJhXa4IeX+HkWPnwJ5FanxEJFZZDTWiDs/Kvyw==",
"version": "5.19.0",
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.19.0.tgz",
"integrity": "sha512-CzOpau+q1kEWQyoQMvlnXIHqPvwmWbh48xZ4n8KWbAql0p8PC0BIgSTYW5ncxXa4JSEff0tcoxSZB874wDstdg==",
"hasInstallScript": true,
"license": "Apache-2.0",
"engines": {
@ -9664,48 +9664,113 @@
}
},
"node_modules/@prisma/debug": {
"version": "5.18.0",
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.18.0.tgz",
"integrity": "sha512-f+ZvpTLidSo3LMJxQPVgAxdAjzv5OpzAo/eF8qZqbwvgi2F5cTOI9XCpdRzJYA0iGfajjwjOKKrVq64vkxEfUw==",
"version": "5.19.0",
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.19.0.tgz",
"integrity": "sha512-+b/G0ubAZlrS+JSiDhXnYV5DF/aTJ3pinktkiV/L4TtLRLZO6SVGyFELgxBsicCTWJ2ZMu5vEV/jTtYCdjFTRA==",
"license": "Apache-2.0"
},
"node_modules/@prisma/engines": {
"version": "5.18.0",
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.18.0.tgz",
"integrity": "sha512-ofmpGLeJ2q2P0wa/XaEgTnX/IsLnvSp/gZts0zjgLNdBhfuj2lowOOPmDcfKljLQUXMvAek3lw5T01kHmCG8rg==",
"version": "5.19.0",
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.19.0.tgz",
"integrity": "sha512-UtW+0m4HYoRSSR3LoDGKF3Ud4BSMWYlLEt4slTnuP1mI+vrV3zaDoiAPmejdAT76vCN5UqnWURbkXxf66nSylQ==",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"@prisma/debug": "5.18.0",
"@prisma/engines-version": "5.18.0-25.4c784e32044a8a016d99474bd02a3b6123742169",
"@prisma/fetch-engine": "5.18.0",
"@prisma/get-platform": "5.18.0"
"@prisma/debug": "5.19.0",
"@prisma/engines-version": "5.19.0-31.5fe21811a6ba0b952a3bc71400666511fe3b902f",
"@prisma/fetch-engine": "5.19.0",
"@prisma/get-platform": "5.19.0"
}
},
"node_modules/@prisma/engines-version": {
"version": "5.18.0-25.4c784e32044a8a016d99474bd02a3b6123742169",
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.18.0-25.4c784e32044a8a016d99474bd02a3b6123742169.tgz",
"integrity": "sha512-a/+LpJj8vYU3nmtkg+N3X51ddbt35yYrRe8wqHTJtYQt7l1f8kjIBcCs6sHJvodW/EK5XGvboOiwm47fmNrbgg==",
"version": "5.19.0-31.5fe21811a6ba0b952a3bc71400666511fe3b902f",
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.19.0-31.5fe21811a6ba0b952a3bc71400666511fe3b902f.tgz",
"integrity": "sha512-GimI9aZIFy/yvvR11KfXRn3pliFn1QAkdebVlsXlnoh5uk0YhLblVmeYiHfsu+wDA7BeKqYT4sFfzg8mutzuWw==",
"license": "Apache-2.0"
},
"node_modules/@prisma/fetch-engine": {
"version": "5.18.0",
"resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.18.0.tgz",
"integrity": "sha512-I/3u0x2n31rGaAuBRx2YK4eB7R/1zCuayo2DGwSpGyrJWsZesrV7QVw7ND0/Suxeo/vLkJ5OwuBqHoCxvTHpOg==",
"version": "5.19.0",
"resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.19.0.tgz",
"integrity": "sha512-oOiPNtmJX0cP/ebu7BBEouJvCw8T84/MFD/Hf2zlqjxkK4ojl38bB9i9J5LAxotL6WlYVThKdxc7HqoWnPOhqQ==",
"license": "Apache-2.0",
"dependencies": {
"@prisma/debug": "5.18.0",
"@prisma/engines-version": "5.18.0-25.4c784e32044a8a016d99474bd02a3b6123742169",
"@prisma/get-platform": "5.18.0"
"@prisma/debug": "5.19.0",
"@prisma/engines-version": "5.19.0-31.5fe21811a6ba0b952a3bc71400666511fe3b902f",
"@prisma/get-platform": "5.19.0"
}
},
"node_modules/@prisma/get-platform": {
"version": "5.18.0",
"resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.18.0.tgz",
"integrity": "sha512-Tk+m7+uhqcKDgnMnFN0lRiH7Ewea0OEsZZs9pqXa7i3+7svS3FSCqDBCaM9x5fmhhkufiG0BtunJVDka+46DlA==",
"version": "5.19.0",
"resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.19.0.tgz",
"integrity": "sha512-s9DWkZKnuP4Y8uy6yZfvqQ/9X3/+2KYf3IZUVZz5OstJdGBJrBlbmIuMl81917wp5TuK/1k2TpHNCEdpYLPKmg==",
"license": "Apache-2.0",
"dependencies": {
"@prisma/debug": "5.18.0"
"@prisma/debug": "5.19.0"
}
},
"node_modules/@redis/bloom": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz",
"integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==",
"license": "MIT",
"peerDependencies": {
"@redis/client": "^1.0.0"
}
},
"node_modules/@redis/client": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.0.tgz",
"integrity": "sha512-aR0uffYI700OEEH4gYnitAnv3vzVGXCFvYfdpu/CJKvk4pHfLPEy/JSZyrpQ+15WhXe1yJRXLtfQ84s4mEXnPg==",
"license": "MIT",
"dependencies": {
"cluster-key-slot": "1.1.2",
"generic-pool": "3.9.0",
"yallist": "4.0.0"
},
"engines": {
"node": ">=14"
}
},
"node_modules/@redis/client/node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"license": "ISC"
},
"node_modules/@redis/graph": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz",
"integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==",
"license": "MIT",
"peerDependencies": {
"@redis/client": "^1.0.0"
}
},
"node_modules/@redis/json": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.7.tgz",
"integrity": "sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ==",
"license": "MIT",
"peerDependencies": {
"@redis/client": "^1.0.0"
}
},
"node_modules/@redis/search": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@redis/search/-/search-1.2.0.tgz",
"integrity": "sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==",
"license": "MIT",
"peerDependencies": {
"@redis/client": "^1.0.0"
}
},
"node_modules/@redis/time-series": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.1.0.tgz",
"integrity": "sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==",
"license": "MIT",
"peerDependencies": {
"@redis/client": "^1.0.0"
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
@ -11468,10 +11533,11 @@
}
},
"node_modules/@types/cache-manager": {
"version": "3.4.2",
"resolved": "https://registry.npmjs.org/@types/cache-manager/-/cache-manager-3.4.2.tgz",
"integrity": "sha512-1IwA74t5ID4KWo0Kndal16MhiPSZgMe1fGc+MLT6j5r+Ab7jku36PFTl4PP6MiWw0BJscM9QpZEo00qixNQoRg==",
"dev": true
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/@types/cache-manager/-/cache-manager-4.0.6.tgz",
"integrity": "sha512-8qL93MF05/xrzFm/LSPtzNEOE1eQF3VwGHAcQEylgp5hDSTe41jtFwbSYAPfyYcVa28y1vYSjIt0c1fLLUiC/Q==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/cacheable-request": {
"version": "6.0.3",
@ -13255,7 +13321,8 @@
"node_modules/async": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz",
"integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw=="
"integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==",
"dev": true
},
"node_modules/asynckit": {
"version": "0.4.0",
@ -14243,41 +14310,50 @@
}
},
"node_modules/cache-manager": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/cache-manager/-/cache-manager-3.4.3.tgz",
"integrity": "sha512-6+Hfzy1SNs/thUwo+07pV0ozgxc4sadrAN0eFVGvXl/X9nz3J0BqEnnEoyxEn8jnF+UkEo0MKpyk9BO80hMeiQ==",
"version": "5.7.6",
"resolved": "https://registry.npmjs.org/cache-manager/-/cache-manager-5.7.6.tgz",
"integrity": "sha512-wBxnBHjDxF1RXpHCBD6HGvKER003Ts7IIm0CHpggliHzN1RZditb7rXoduE1rplc2DEFYKxhLKgFuchXMJje9w==",
"license": "MIT",
"dependencies": {
"async": "3.2.0",
"lodash": "^4.17.21",
"lru-cache": "6.0.0"
}
},
"node_modules/cache-manager-redis-store": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/cache-manager-redis-store/-/cache-manager-redis-store-2.0.0.tgz",
"integrity": "sha512-bWLWlUg6nCYHiJLCCYxY2MgvwvKnvlWwrbuynrzpjEIhfArD2GC9LtutIHFEPeyGVQN6C+WEw+P3r+BFBwhswg==",
"dependencies": {
"redis": "^3.0.2"
"eventemitter3": "^5.0.1",
"lodash.clonedeep": "^4.5.0",
"lru-cache": "^10.2.2",
"promise-coalesce": "^1.1.2"
},
"engines": {
"node": ">= 8.3"
"node": ">= 18"
}
},
"node_modules/cache-manager-redis-yet": {
"version": "5.1.4",
"resolved": "https://registry.npmjs.org/cache-manager-redis-yet/-/cache-manager-redis-yet-5.1.4.tgz",
"integrity": "sha512-2mXZjo+txfH2m+mSTHTITNq8c5SssU2nP7NutzrocO3Mw/SbjHcDo+mriI3ZuR63ov/oUUIaF9iF+MzDqVzMoQ==",
"license": "MIT",
"dependencies": {
"@redis/bloom": "^1.2.0",
"@redis/client": "^1.6.0",
"@redis/graph": "^1.1.1",
"@redis/json": "^1.0.7",
"@redis/search": "^1.2.0",
"@redis/time-series": "^1.1.0",
"cache-manager": "^5.7.6",
"redis": "^4.7.0"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/cache-manager/node_modules/eventemitter3": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
"license": "MIT"
},
"node_modules/cache-manager/node_modules/lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/cache-manager/node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
"version": "10.4.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
"license": "ISC"
},
"node_modules/cacheable-lookup": {
"version": "5.0.4",
@ -19466,6 +19542,15 @@
"node": ">=10"
}
},
"node_modules/generic-pool": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz",
"integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==",
"license": "MIT",
"engines": {
"node": ">= 4"
}
},
"node_modules/gensync": {
"version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
@ -24555,6 +24640,12 @@
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
"optional": true
},
"node_modules/lodash.clonedeep": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
"integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==",
"license": "MIT"
},
"node_modules/lodash.clonedeepwith": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.clonedeepwith/-/lodash.clonedeepwith-4.5.0.tgz",
@ -28738,19 +28829,22 @@
}
},
"node_modules/prisma": {
"version": "5.18.0",
"resolved": "https://registry.npmjs.org/prisma/-/prisma-5.18.0.tgz",
"integrity": "sha512-+TrSIxZsh64OPOmaSgVPH7ALL9dfU0jceYaMJXsNrTkFHO7/3RANi5K2ZiPB1De9+KDxCWn7jvRq8y8pvk+o9g==",
"version": "5.19.0",
"resolved": "https://registry.npmjs.org/prisma/-/prisma-5.19.0.tgz",
"integrity": "sha512-Pu7lUKpVyTx8cVwM26dYh8NdvMOkMnJXzE8L6cikFuR4JwyMU5NKofQkWyxJKlTT4fNjmcnibTvklV8oVMrn+g==",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"@prisma/engines": "5.18.0"
"@prisma/engines": "5.19.0"
},
"bin": {
"prisma": "build/index.js"
},
"engines": {
"node": ">=16.13"
},
"optionalDependencies": {
"fsevents": "2.3.3"
}
},
"node_modules/prismjs": {
@ -28785,6 +28879,15 @@
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
},
"node_modules/promise-coalesce": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/promise-coalesce/-/promise-coalesce-1.1.2.tgz",
"integrity": "sha512-zLaJ9b8hnC564fnJH6NFSOGZYYdzrAJn2JUUIwzoQb32fG2QAakpDNM+CZo1km6keXkRXRM+hml1BFAPVnPkxg==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=16"
}
},
"node_modules/promise-inflight": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
@ -29125,28 +29228,22 @@
}
},
"node_modules/redis": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/redis/-/redis-3.1.2.tgz",
"integrity": "sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw==",
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/redis/-/redis-4.7.0.tgz",
"integrity": "sha512-zvmkHEAdGMn+hMRXuMBtu4Vo5P6rHQjLoHftu+lBqq8ZTA3RCVC/WzD790bkKKiNFp7d5/9PcSD19fJyyRvOdQ==",
"license": "MIT",
"workspaces": [
"./packages/*"
],
"dependencies": {
"denque": "^1.5.0",
"redis-commands": "^1.7.0",
"redis-errors": "^1.2.0",
"redis-parser": "^3.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/node-redis"
"@redis/bloom": "1.2.0",
"@redis/client": "1.6.0",
"@redis/graph": "1.1.1",
"@redis/json": "1.0.7",
"@redis/search": "1.2.0",
"@redis/time-series": "1.1.0"
}
},
"node_modules/redis-commands": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz",
"integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ=="
},
"node_modules/redis-errors": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz",
@ -29166,14 +29263,6 @@
"node": ">=4"
}
},
"node_modules/redis/node_modules/denque": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz",
"integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==",
"engines": {
"node": ">=0.10"
}
},
"node_modules/reflect-metadata": {
"version": "0.1.13",
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz",

View File

@ -1,6 +1,6 @@
{
"name": "ghostfolio",
"version": "2.106.0-beta.3",
"version": "2.106.0-beta.5",
"homepage": "https://ghostfol.io",
"license": "AGPL-3.0",
"repository": "https://github.com/ghostfolio/ghostfolio",
@ -74,7 +74,7 @@
"@dinero.js/currencies": "2.0.0-alpha.8",
"@internationalized/number": "3.5.2",
"@nestjs/bull": "10.0.1",
"@nestjs/cache-manager": "2.1.0",
"@nestjs/cache-manager": "2.2.2",
"@nestjs/common": "10.1.3",
"@nestjs/config": "3.0.0",
"@nestjs/core": "10.1.3",
@ -84,7 +84,7 @@
"@nestjs/platform-express": "10.1.3",
"@nestjs/schedule": "3.0.2",
"@nestjs/serve-static": "4.0.0",
"@prisma/client": "5.18.0",
"@prisma/client": "5.19.0",
"@simplewebauthn/browser": "9.0.1",
"@simplewebauthn/server": "9.0.3",
"@stripe/stripe-js": "3.5.0",
@ -93,8 +93,8 @@
"body-parser": "1.20.2",
"bootstrap": "4.6.0",
"bull": "4.10.4",
"cache-manager": "3.4.3",
"cache-manager-redis-store": "2.0.0",
"cache-manager": "5.7.6",
"cache-manager-redis-yet": "5.1.4",
"chart.js": "4.2.0",
"chartjs-adapter-date-fns": "3.0.0",
"chartjs-chart-treemap": "2.3.1",
@ -128,7 +128,7 @@
"passport": "0.7.0",
"passport-google-oauth20": "2.0.0",
"passport-jwt": "4.0.1",
"prisma": "5.18.0",
"prisma": "5.19.0",
"reflect-metadata": "0.1.13",
"rxjs": "7.5.6",
"stripe": "15.11.0",
@ -170,7 +170,7 @@
"@trivago/prettier-plugin-sort-imports": "4.3.0",
"@types/big.js": "6.2.2",
"@types/body-parser": "1.19.5",
"@types/cache-manager": "3.4.2",
"@types/cache-manager": "4.0.6",
"@types/color": "3.0.6",
"@types/google-spreadsheet": "3.1.5",
"@types/jest": "29.4.4",