|
|
|
@ -31,9 +31,12 @@ import { |
|
|
|
DEFAULT_LOCALE, |
|
|
|
PROPERTY_API_KEY_GHOSTFOLIO, |
|
|
|
PROPERTY_IS_READ_ONLY_MODE, |
|
|
|
PROPERTY_MAX_DAILY_REQUESTS, |
|
|
|
PROPERTY_REFERRAL_PARTNERS, |
|
|
|
PROPERTY_SYSTEM_MESSAGE, |
|
|
|
TAG_ID_EXCLUDE_FROM_ANALYSIS |
|
|
|
TAG_ID_EXCLUDE_FROM_ANALYSIS, |
|
|
|
THROTTLE_DAILY_KEY, |
|
|
|
THROTTLE_DAILY_TTL |
|
|
|
} from '@ghostfolio/common/config'; |
|
|
|
import { SubscriptionType } from '@ghostfolio/common/enums'; |
|
|
|
import { |
|
|
|
@ -50,15 +53,18 @@ import { |
|
|
|
import { UserWithSettings } from '@ghostfolio/common/types'; |
|
|
|
import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; |
|
|
|
|
|
|
|
import { Injectable } from '@nestjs/common'; |
|
|
|
import { Injectable, Logger } from '@nestjs/common'; |
|
|
|
import { EventEmitter2 } from '@nestjs/event-emitter'; |
|
|
|
import { InjectThrottlerStorage, ThrottlerStorage } from '@nestjs/throttler'; |
|
|
|
import { Prisma, Role, Settings, User } from '@prisma/client'; |
|
|
|
import { differenceInDays, subDays } from 'date-fns'; |
|
|
|
import { without } from 'lodash'; |
|
|
|
import { isNil, without } from 'lodash'; |
|
|
|
import { createHmac } from 'node:crypto'; |
|
|
|
|
|
|
|
@Injectable() |
|
|
|
export class UserService { |
|
|
|
private readonly logger = new Logger(UserService.name); |
|
|
|
|
|
|
|
public constructor( |
|
|
|
private readonly activitiesService: ActivitiesService, |
|
|
|
private readonly configurationService: ConfigurationService, |
|
|
|
@ -67,7 +73,9 @@ export class UserService { |
|
|
|
private readonly prismaService: PrismaService, |
|
|
|
private readonly propertyService: PropertyService, |
|
|
|
private readonly subscriptionService: SubscriptionService, |
|
|
|
private readonly tagService: TagService |
|
|
|
private readonly tagService: TagService, |
|
|
|
@InjectThrottlerStorage() |
|
|
|
private readonly throttlerStorage: ThrottlerStorage |
|
|
|
) {} |
|
|
|
|
|
|
|
public async count(args?: Prisma.UserCountArgs) { |
|
|
|
@ -228,6 +236,38 @@ export class UserService { |
|
|
|
return usersWithAdminRole.length > 0; |
|
|
|
} |
|
|
|
|
|
|
|
public async isDailyRequestLimitExceeded({ |
|
|
|
user |
|
|
|
}: { |
|
|
|
user: UserWithSettings; |
|
|
|
}) { |
|
|
|
if (user.subscription?.type === SubscriptionType.Premium) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
const maxDailyRequests = await this.getMaxDailyRequests(); |
|
|
|
|
|
|
|
if (maxDailyRequests === undefined) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
try { |
|
|
|
const { isBlocked } = await this.throttlerStorage.increment( |
|
|
|
`${THROTTLE_DAILY_KEY}-${user.id}`, |
|
|
|
THROTTLE_DAILY_TTL, |
|
|
|
maxDailyRequests, |
|
|
|
THROTTLE_DAILY_TTL, |
|
|
|
THROTTLE_DAILY_KEY |
|
|
|
); |
|
|
|
|
|
|
|
return isBlocked; |
|
|
|
} catch (error) { |
|
|
|
this.logger.error(error); |
|
|
|
|
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public async user( |
|
|
|
userWhereUniqueInput: Prisma.UserWhereUniqueInput |
|
|
|
): Promise<UserWithSettings | null> { |
|
|
|
@ -782,4 +822,26 @@ export class UserService { |
|
|
|
|
|
|
|
return settings; |
|
|
|
} |
|
|
|
|
|
|
|
private async getMaxDailyRequests() { |
|
|
|
const value = await this.propertyService.getByKey<string>( |
|
|
|
PROPERTY_MAX_DAILY_REQUESTS |
|
|
|
); |
|
|
|
|
|
|
|
if (isNil(value) || value === '') { |
|
|
|
return undefined; |
|
|
|
} |
|
|
|
|
|
|
|
const maxDailyRequests = Number(value); |
|
|
|
|
|
|
|
if (!Number.isInteger(maxDailyRequests) || maxDailyRequests < 0) { |
|
|
|
this.logger.warn( |
|
|
|
`The property ${PROPERTY_MAX_DAILY_REQUESTS} is not a non-negative integer ("${value}"), the daily request limit is not applied` |
|
|
|
); |
|
|
|
|
|
|
|
return undefined; |
|
|
|
} |
|
|
|
|
|
|
|
return maxDailyRequests; |
|
|
|
} |
|
|
|
} |
|
|
|
|