Browse Source

Merge 360d5c397b into ad47bedcb5

pull/4270/merge
Haruka Kishida 1 week ago
committed by GitHub
parent
commit
369f16f35a
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      CHANGELOG.md
  2. 55
      apps/api/src/app/auth/api-key.strategy.ts
  3. 16
      apps/api/src/app/redis-cache/redis-cache.module.ts
  4. 36
      apps/api/src/app/redis-cache/redis-cache.service.ts
  5. 6143
      package-lock.json
  6. 30
      package.json

1
CHANGELOG.md

@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Upgraded `nestjs` from version `10.4.15` to `11.0.12`
- Upgraded `eslint` dependencies
## 2.150.0 - 2025-04-05

55
apps/api/src/app/auth/api-key.strategy.ts

@ -21,37 +21,38 @@ export class ApiKeyStrategy extends PassportStrategy(
private readonly prismaService: PrismaService,
private readonly userService: UserService
) {
super(
{ header: HEADER_KEY_TOKEN, prefix: 'Api-Key ' },
true,
async (apiKey: string, done: (error: any, user?: any) => void) => {
try {
const user = await this.validateApiKey(apiKey);
if (this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION')) {
if (hasRole(user, 'INACTIVE')) {
throw new HttpException(
getReasonPhrase(StatusCodes.TOO_MANY_REQUESTS),
StatusCodes.TOO_MANY_REQUESTS
);
}
super({ header: HEADER_KEY_TOKEN, prefix: 'Api-Key ' }, true);
}
await this.prismaService.analytics.upsert({
create: { User: { connect: { id: user.id } } },
update: {
activityCount: { increment: 1 },
lastRequestAt: new Date()
},
where: { userId: user.id }
});
}
public async validate(
apiKey: string,
done: (error: any, user?: any) => void
) {
try {
const user = await this.validateApiKey(apiKey);
done(null, user);
} catch (error) {
done(error, null);
if (this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION')) {
if (hasRole(user, 'INACTIVE')) {
throw new HttpException(
getReasonPhrase(StatusCodes.TOO_MANY_REQUESTS),
StatusCodes.TOO_MANY_REQUESTS
);
}
await this.prismaService.analytics.upsert({
create: { User: { connect: { id: user.id } } },
update: {
activityCount: { increment: 1 },
lastRequestAt: new Date()
},
where: { userId: user.id }
});
}
);
done(null, user);
} catch (error) {
done(error, null);
}
}
private async validateApiKey(apiKey: string) {

16
apps/api/src/app/redis-cache/redis-cache.module.ts

@ -1,17 +1,16 @@
import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module';
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
import { createKeyv } from '@keyv/redis';
import { CacheModule } from '@nestjs/cache-manager';
import { Module } from '@nestjs/common';
import { redisStore } from 'cache-manager-redis-yet';
import type { RedisClientOptions } from 'redis';
import { RedisCacheService } from './redis-cache.service';
@Module({
exports: [RedisCacheService],
imports: [
CacheModule.registerAsync<RedisClientOptions>({
CacheModule.registerAsync({
imports: [ConfigurationModule],
inject: [ConfigurationService],
useFactory: async (configurationService: ConfigurationService) => {
@ -20,10 +19,13 @@ import { RedisCacheService } from './redis-cache.service';
);
return {
store: redisStore,
ttl: configurationService.get('CACHE_TTL'),
url: `redis://${redisPassword ? `:${redisPassword}` : ''}@${configurationService.get('REDIS_HOST')}:${configurationService.get('REDIS_PORT')}/${configurationService.get('REDIS_DB')}`
} as RedisClientOptions;
stores: [
createKeyv(
`redis://${redisPassword ? `:${redisPassword}` : ''}@${configurationService.get('REDIS_HOST')}:${configurationService.get('REDIS_PORT')}/${configurationService.get('REDIS_DB')}`
)
],
ttl: configurationService.get('CACHE_TTL')
};
}
}),
ConfigurationModule

36
apps/api/src/app/redis-cache/redis-cache.service.ts

@ -2,21 +2,20 @@ import { ConfigurationService } from '@ghostfolio/api/services/configuration/con
import { getAssetProfileIdentifier } from '@ghostfolio/common/helper';
import { AssetProfileIdentifier, Filter } from '@ghostfolio/common/interfaces';
import { CACHE_MANAGER } from '@nestjs/cache-manager';
import { CACHE_MANAGER, Cache } 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 ms from 'ms';
@Injectable()
export class RedisCacheService {
public constructor(
@Inject(CACHE_MANAGER) private readonly cache: RedisCache,
@Inject(CACHE_MANAGER) private readonly cache: Cache,
private readonly configurationService: ConfigurationService
) {
const client = cache.store.client;
const client = cache.stores[0];
client.deserialize = undefined;
client.on('error', (error) => {
Logger.error(error, 'RedisCacheService');
});
@ -27,13 +26,15 @@ export class RedisCacheService {
}
public async getKeys(aPrefix?: string): Promise<string[]> {
let prefix = aPrefix;
if (prefix) {
prefix = `${prefix}*`;
const keys: string[] = [];
const prefix = aPrefix;
for await (const [key] of this.cache.stores[0].iterator({})) {
if ((prefix && key.startsWith(prefix)) || !prefix) {
keys.push(key);
}
}
return this.cache.store.keys(prefix);
return keys;
}
public getPortfolioSnapshotKey({
@ -62,10 +63,8 @@ export class RedisCacheService {
public async isHealthy() {
try {
const client = this.cache.store.client;
const isHealthy = await Promise.race([
client.ping(),
this.getKeys(),
new Promise((_, reject) =>
setTimeout(
() => reject(new Error('Redis health check timeout')),
@ -92,17 +91,16 @@ export class RedisCacheService {
const keys = await this.getKeys(
`${this.getPortfolioSnapshotKey({ userId })}`
);
for (const key of keys) {
await this.remove(key);
}
console.log('keys is ');
console.log(keys);
return this.cache.mdel(keys);
}
public async reset() {
return this.cache.reset();
return this.cache.clear();
}
public async set(key: string, value: string, ttl?: Milliseconds) {
public async set(key: string, value: string, ttl?: number) {
return this.cache.set(
key,
value,

6143
package-lock.json

File diff suppressed because it is too large

30
package.json

@ -76,17 +76,18 @@
"@dfinity/principal": "0.15.7",
"@dinero.js/currencies": "2.0.0-alpha.8",
"@internationalized/number": "3.6.0",
"@nestjs/bull": "10.2.3",
"@nestjs/cache-manager": "2.3.0",
"@nestjs/common": "10.4.15",
"@nestjs/config": "3.3.0",
"@nestjs/core": "10.4.15",
"@nestjs/event-emitter": "2.1.1",
"@nestjs/jwt": "10.2.0",
"@nestjs/passport": "10.0.3",
"@nestjs/platform-express": "10.4.15",
"@nestjs/schedule": "4.1.2",
"@nestjs/serve-static": "4.0.2",
"@keyv/redis": "4.3.2",
"@nestjs/bull": "11.0.2",
"@nestjs/cache-manager": "3.0.1",
"@nestjs/common": "11.0.12",
"@nestjs/config": "4.0.2",
"@nestjs/core": "11.0.12",
"@nestjs/event-emitter": "3.0.1",
"@nestjs/jwt": "11.0.0",
"@nestjs/passport": "11.0.5",
"@nestjs/platform-express": "11.0.12",
"@nestjs/schedule": "5.0.1",
"@nestjs/serve-static": "5.0.3",
"@prisma/client": "6.5.0",
"@simplewebauthn/browser": "13.1.0",
"@simplewebauthn/server": "13.1.1",
@ -95,8 +96,6 @@
"big.js": "6.2.2",
"bootstrap": "4.6.0",
"bull": "4.16.5",
"cache-manager": "5.7.6",
"cache-manager-redis-yet": "5.1.4",
"chart.js": "4.4.7",
"chartjs-adapter-date-fns": "3.0.0",
"chartjs-chart-treemap": "3.1.0",
@ -153,8 +152,8 @@
"@angular/pwa": "19.2.1",
"@eslint/eslintrc": "3.3.1",
"@eslint/js": "9.24.0",
"@nestjs/schematics": "10.2.3",
"@nestjs/testing": "10.4.15",
"@nestjs/schematics": "11.0.2",
"@nestjs/testing": "11.0.12",
"@nx/angular": "20.6.4",
"@nx/cypress": "20.6.4",
"@nx/eslint-plugin": "20.6.4",
@ -173,7 +172,6 @@
"@storybook/core-server": "8.4.7",
"@trivago/prettier-plugin-sort-imports": "5.2.2",
"@types/big.js": "6.2.2",
"@types/cache-manager": "4.0.6",
"@types/google-spreadsheet": "3.1.5",
"@types/jest": "29.5.13",
"@types/lodash": "4.17.16",

Loading…
Cancel
Save