Browse Source

Add daily request limit

pull/7487/head
Thomas Kaul 1 month ago
parent
commit
6f4754d975
  1. 7
      apps/api/src/app/auth/api-key.strategy.ts
  2. 7
      apps/api/src/app/auth/jwt.strategy.ts
  3. 1
      apps/api/src/app/portfolio/portfolio.service.spec.ts
  4. 44
      apps/api/src/app/user/user.service.ts

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

@ -35,6 +35,13 @@ export class ApiKeyStrategy extends PassportStrategy(
); );
} }
if (await this.userService.isDailyRequestLimitExceeded({ user })) {
throw new HttpException(
getReasonPhrase(StatusCodes.TOO_MANY_REQUESTS),
StatusCodes.TOO_MANY_REQUESTS
);
}
await this.prismaService.analytics.upsert({ await this.prismaService.analytics.upsert({
create: { user: { connect: { id: user.id } } }, create: { user: { connect: { id: user.id } } },
update: { update: {

7
apps/api/src/app/auth/jwt.strategy.ts

@ -42,6 +42,13 @@ export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
); );
} }
if (await this.userService.isDailyRequestLimitExceeded({ user })) {
throw new HttpException(
getReasonPhrase(StatusCodes.TOO_MANY_REQUESTS),
StatusCodes.TOO_MANY_REQUESTS
);
}
const country = const country =
countriesAndTimezones.getCountryForTimezone(timezone)?.id; countriesAndTimezones.getCountryForTimezone(timezone)?.id;

1
apps/api/src/app/portfolio/portfolio.service.spec.ts

@ -92,6 +92,7 @@ describe('PortfolioService', () => {
null, null,
null, null,
null, null,
null,
null null
); );

44
apps/api/src/app/user/user.service.ts

@ -31,9 +31,12 @@ import {
DEFAULT_LOCALE, DEFAULT_LOCALE,
PROPERTY_API_KEY_GHOSTFOLIO, PROPERTY_API_KEY_GHOSTFOLIO,
PROPERTY_IS_READ_ONLY_MODE, PROPERTY_IS_READ_ONLY_MODE,
PROPERTY_MAX_DAILY_REQUESTS,
PROPERTY_REFERRAL_PARTNERS, PROPERTY_REFERRAL_PARTNERS,
PROPERTY_SYSTEM_MESSAGE, PROPERTY_SYSTEM_MESSAGE,
TAG_ID_EXCLUDE_FROM_ANALYSIS TAG_ID_EXCLUDE_FROM_ANALYSIS,
THROTTLE_DAILY_KEY,
THROTTLE_DAILY_TTL
} from '@ghostfolio/common/config'; } from '@ghostfolio/common/config';
import { SubscriptionType } from '@ghostfolio/common/enums'; import { SubscriptionType } from '@ghostfolio/common/enums';
import { import {
@ -52,6 +55,7 @@ import { PerformanceCalculationType } from '@ghostfolio/common/types/performance
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { EventEmitter2 } from '@nestjs/event-emitter'; import { EventEmitter2 } from '@nestjs/event-emitter';
import { InjectThrottlerStorage, ThrottlerStorage } from '@nestjs/throttler';
import { Prisma, Role, Settings, User } from '@prisma/client'; import { Prisma, Role, Settings, User } from '@prisma/client';
import { differenceInDays, subDays } from 'date-fns'; import { differenceInDays, subDays } from 'date-fns';
import { without } from 'lodash'; import { without } from 'lodash';
@ -67,7 +71,9 @@ export class UserService {
private readonly prismaService: PrismaService, private readonly prismaService: PrismaService,
private readonly propertyService: PropertyService, private readonly propertyService: PropertyService,
private readonly subscriptionService: SubscriptionService, private readonly subscriptionService: SubscriptionService,
private readonly tagService: TagService private readonly tagService: TagService,
@InjectThrottlerStorage()
private readonly throttlerStorage: ThrottlerStorage
) {} ) {}
public async count(args?: Prisma.UserCountArgs) { public async count(args?: Prisma.UserCountArgs) {
@ -228,6 +234,32 @@ export class UserService {
return usersWithAdminRole.length > 0; 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) {
return false;
}
const { isBlocked } = await this.throttlerStorage.increment(
`${THROTTLE_DAILY_KEY}-${user.id}`,
THROTTLE_DAILY_TTL,
maxDailyRequests,
THROTTLE_DAILY_TTL,
THROTTLE_DAILY_KEY
);
return isBlocked;
}
public async user( public async user(
userWhereUniqueInput: Prisma.UserWhereUniqueInput userWhereUniqueInput: Prisma.UserWhereUniqueInput
): Promise<UserWithSettings | null> { ): Promise<UserWithSettings | null> {
@ -782,4 +814,12 @@ export class UserService {
return settings; return settings;
} }
private async getMaxDailyRequests() {
const value = await this.propertyService.getByKey<string>(
PROPERTY_MAX_DAILY_REQUESTS
);
return Number(value) || undefined;
}
} }

Loading…
Cancel
Save