Skip to content
This repository has been archived by the owner on Apr 19, 2023. It is now read-only.

Commit

Permalink
♻️ Use Configuration interface in service
Browse files Browse the repository at this point in the history
  • Loading branch information
AnandChowdhary committed Dec 1, 2020
1 parent f8eb9ca commit d2ea77b
Show file tree
Hide file tree
Showing 15 changed files with 118 additions and 92 deletions.
14 changes: 11 additions & 3 deletions src/modules/api-keys/api-keys.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { ConfigService } from '@nestjs/config';
import type { InputJsonValue, JsonValue, Prisma } from '@prisma/client';
import { ApiKey } from '@prisma/client';
import QuickLRU from 'quick-lru';
import { Configuration } from '../../config/configuration.interface';
import {
API_KEY_NOT_FOUND,
UNAUTHORIZED_RESOURCE,
Expand All @@ -20,7 +21,10 @@ import { StripeService } from '../stripe/stripe.service';
@Injectable()
export class ApiKeysService {
private lru = new QuickLRU<string, ApiKey>({
maxSize: this.configService.get<number>('caching.apiKeyLruSize') ?? 100,
maxSize:
this.configService.get<Configuration['caching']['apiKeyLruSize']>(
'caching.apiKeyLruSize',
) ?? 100,
});

constructor(
Expand Down Expand Up @@ -307,10 +311,14 @@ export class ApiKeysService {
const now = new Date();
now.setDate(
now.getDate() -
this.configService.get<number>('tracking.deleteOldLogsDays'),
this.configService.get<Configuration['tracking']['deleteOldLogsDays']>(
'tracking.deleteOldLogsDays',
),
);
const result = await this.elasticSearchService.search({
index: this.configService.get<string>('tracking.index'),
index: this.configService.get<Configuration['tracking']['index']>(
'tracking.index',
),
from: params.cursor?.id,
body: {
query: {
Expand Down
5 changes: 4 additions & 1 deletion src/modules/approved-subnets/approved-subnets.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type { Prisma } from '@prisma/client';
import { ApprovedSubnet } from '@prisma/client';
import { compare, hash } from 'bcrypt';
import anonymize from 'ip-anonymize';
import { Configuration } from '../../config/configuration.interface';
import {
APPROVED_SUBNET_NOT_FOUND,
UNAUTHORIZED_RESOURCE,
Expand Down Expand Up @@ -81,7 +82,9 @@ export class ApprovedSubnetsService {
async approveNewSubnet(userId: number, ipAddress: string) {
const subnet = await hash(
anonymize(ipAddress),
this.configService.get<number>('security.saltRounds') ?? 10,
this.configService.get<Configuration['security']['saltRounds']>(
'security.saltRounds',
) ?? 10,
);
const location = await this.geolocationService.getLocation(ipAddress);
const approved = await this.prisma.approvedSubnet.create({
Expand Down
20 changes: 9 additions & 11 deletions src/modules/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
Injectable,
NotFoundException,
UnauthorizedException,
UnprocessableEntityException
UnprocessableEntityException,
} from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Authenticator } from '@otplib/core';
Expand Down Expand Up @@ -34,7 +34,7 @@ import {
SESSION_NOT_FOUND,
UNVERIFIED_EMAIL,
UNVERIFIED_LOCATION,
USER_NOT_FOUND
USER_NOT_FOUND,
} from '../../errors/errors.constants';
import { safeEmail } from '../../helpers/safe-email';
import { GeolocationService } from '../../providers/geolocation/geolocation.service';
Expand All @@ -49,7 +49,7 @@ import {
LOGIN_ACCESS_TOKEN,
MERGE_ACCOUNTS_TOKEN,
MULTI_FACTOR_TOKEN,
PASSWORD_RESET_TOKEN
PASSWORD_RESET_TOKEN,
} from '../../providers/tokens/tokens.constants';
import { TokensService } from '../../providers/tokens/tokens.service';
import { TwilioService } from '../../providers/twilio/twilio.service';
Expand All @@ -59,7 +59,7 @@ import {
AccessTokenClaims,
MfaTokenPayload,
TokenResponse,
TotpTokenResponse
TotpTokenResponse,
} from './auth.interface';

@Injectable()
Expand Down Expand Up @@ -286,7 +286,7 @@ export class AuthService {
});
const otpauth = this.authenticator.keyuri(
userId.toString(),
this.configService.get<string>('meta.appName') ?? '',
this.metaConfig.appName ?? '',
secret,
);
return qrcode.toDataURL(otpauth);
Expand Down Expand Up @@ -554,23 +554,21 @@ export class AuthService {
const totpToken = this.tokensService.signJwt(
MULTI_FACTOR_TOKEN,
mfaTokenPayload,
this.configService.get<string>('security.mfaTokenExpiry'),
this.securityConfig.mfaTokenExpiry,
);
if (user.twoFactorMethod === 'EMAIL' || forceMethod === 'EMAIL') {
this.email.send({
to: `"${user.name}" <${user.prefersEmail.email}>`,
template: 'auth/mfa-code',
data: {
name: user.name,
minutes: parseInt(
this.configService.get<string>('security.mfaTokenExpiry') ?? '',
),
minutes: parseInt(this.securityConfig.mfaTokenExpiry),
link: `${
this.metaConfig.frontendUrl
}/auth/link/login%2Ftoken?token=${this.tokensService.signJwt(
EMAIL_MFA_TOKEN,
{ id: user.id },
'30m',
this.securityConfig.mfaTokenExpiry,
)}`,
},
});
Expand All @@ -580,7 +578,7 @@ export class AuthService {
this.twilioService.send({
to: user.twoFactorPhone,
body: `${this.getOneTimePassword(user.twoFactorSecret)} is your ${
this.configService.get<string>('meta.appName') ?? ''
this.metaConfig.appName ?? ''
} verification code.`,
});
}
Expand Down
8 changes: 7 additions & 1 deletion src/modules/auth/staart.strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import minimatch from 'minimatch';
import { Strategy } from 'passport-strategy';
import { getClientIp } from 'request-ip';
import { validate } from 'uuid';
import { Configuration } from '../../config/configuration.interface';
import { LOGIN_ACCESS_TOKEN } from '../../providers/tokens/tokens.constants';
import { TokensService } from '../../providers/tokens/tokens.service';
import { ApiKeysService } from '../api-keys/api-keys.service';
Expand Down Expand Up @@ -83,7 +84,12 @@ export class StaartStrategy extends PassportStrategy(StaartStrategyName) {
) as AccessTokenClaims;
const { sub, scopes } = payload;
const [userPart, hostPart] = sub.split('@');
if (hostPart !== this.configService.get('security.issuerDomain'))
if (
hostPart !==
this.configService.get<Configuration['security']['issuerDomain']>(
'security.issuerDomain',
)
)
throw new Error('Invalid issuer domain');
const id = parseInt(userPart.replace('acct:', ''));
if (isNaN(id)) throw new Error('Invalid user ID');
Expand Down
7 changes: 4 additions & 3 deletions src/modules/domains/domains.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { ConfigService } from '@nestjs/config';
import type { Prisma } from '@prisma/client';
import { Domain } from '@prisma/client';
import got from 'got';
import { Configuration } from '../../config/configuration.interface';
import { URL } from 'url';
import {
DOMAIN_NOT_FOUND,
Expand Down Expand Up @@ -130,9 +131,9 @@ export class DomainsService {
let verified = false;
try {
const { body } = await got(
`http://${domain.domain}/.well-known/${this.configService.get<string>(
'meta.domainVerificationFile' ?? 'staart-verify.txt',
)}`,
`http://${domain.domain}/.well-known/${this.configService.get<
Configuration['meta']['domainVerificationFile']
>('meta.domainVerificationFile' ?? 'staart-verify.txt')}`,
);
verified = body.includes(domain.verificationCode);
} catch (error) {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { ConfigService } from '@nestjs/config';
import type { MfaMethod } from '@prisma/client';
import { User } from '@prisma/client';
import { hash } from 'bcrypt';
import { Configuration } from '../../config/configuration.interface';
import {
MFA_ENABLED_CONFLICT,
MFA_NOT_ENABLED,
Expand Down Expand Up @@ -58,9 +59,11 @@ export class MultiFactorAuthenticationService {
});
return this.twilioService.send({
to: phone,
body: `${this.auth.getOneTimePassword(secret)} is your ${
this.configService.get<string>('meta.appName') ?? ''
} verification code.`,
body: `${this.auth.getOneTimePassword(
secret,
)} is your ${this.configService.get<Configuration['meta']['appName']>(
'meta.appName',
)} verification code.`,
});
}

Expand Down Expand Up @@ -125,7 +128,9 @@ export class MultiFactorAuthenticationService {
codes.push(unsafeCode);
const code = await hash(
unsafeCode,
this.configService.get<number>('security.saltRounds') ?? 10,
this.configService.get<Configuration['security']['saltRounds']>(
'security.saltRounds',
) ?? 10,
);
await this.prisma.backupCode.create({
data: { user: { connect: { id } }, code },
Expand Down
15 changes: 7 additions & 8 deletions src/modules/stripe/stripe.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,17 @@ export class StripeService {
stripe: Stripe;
logger = new Logger(StripeService.name);
private metaConfig = this.configService.get<Configuration['meta']>('meta');
private paymentsConfig = this.configService.get<Configuration['payments']>(
'payments',
);

constructor(
private configService: ConfigService,
private prisma: PrismaService,
) {
const stripeApiKey = this.configService.get<string>(
'payments.stripeApiKey',
);
this.stripe = new Stripe(stripeApiKey, {
this.stripe = new Stripe(this.paymentsConfig.stripeApiKey, {
apiVersion: '2020-08-27',
typescript: true,
});
}

Expand Down Expand Up @@ -183,9 +184,7 @@ export class StripeService {
const data: Stripe.Checkout.SessionCreateParams = {
customer: stripeId,
mode,
payment_method_types: this.configService.get<
Array<Stripe.Checkout.SessionCreateParams.PaymentMethodType>
>('payments.paymentMethodTypes') ?? ['card'],
payment_method_types: this.paymentsConfig.paymentMethodTypes ?? ['card'],
success_url: `${this.metaConfig.frontendUrl}/groups/${groupId}`,
cancel_url: `${this.metaConfig.frontendUrl}/groups/${groupId}`,
};
Expand Down Expand Up @@ -235,7 +234,7 @@ export class StripeService {
const event = this.stripe.webhooks.constructEvent(
payload,
signature,
this.configService.get<string>('payments.stripeEndpointSecret') ?? '',
this.paymentsConfig.stripeEndpointSecret,
);
switch (event.type) {
default:
Expand Down
5 changes: 4 additions & 1 deletion src/modules/webhooks/webhooks.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Webhook } from '@prisma/client';
import got from 'got';
import PQueue from 'p-queue';
import pRetry from 'p-retry';
import { Configuration } from '../../config/configuration.interface';
import {
UNAUTHORIZED_RESOURCE,
WEBHOOK_NOT_FOUND,
Expand Down Expand Up @@ -155,7 +156,9 @@ export class WebhooksService {
.add(() =>
pRetry(() => this.callWebhook(webhook, event), {
retries:
this.configService.get<number>('webhooks.retries') ?? 3,
this.configService.get<Configuration['webhooks']['retries']>(
'webhooks.retries',
) ?? 3,
onFailedAttempt: (error) => {
this.logger.error(
`Triggering webhoook failed, retrying (${error.retriesLeft} attempts left)`,
Expand Down
25 changes: 12 additions & 13 deletions src/providers/elasticsearch/elasticsearch.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,26 @@ import { Configuration } from '../../config/configuration.interface';
export class ElasticSearchService {
private logger = new Logger(ElasticSearchService.name);
private queue = new PQueue({ concurrency: 1 });
private elasticSearchConfig = this.configService.get<
Configuration['elasticSearch']
>('elasticSearch');
client?: Client;

constructor(private configService: ConfigService) {
const config = this.configService.get<Configuration['elasticSearch']>(
'elasticSearch',
);
if (config.aws?.accessKeyId) {
if (this.elasticSearchConfig.aws?.accessKeyId) {
AWS.config.update({
accessKeyId: config.aws.accessKeyId,
secretAccessKey: config.aws.secretAccessKey,
region: config.aws.region,
accessKeyId: this.elasticSearchConfig.aws.accessKeyId,
secretAccessKey: this.elasticSearchConfig.aws.secretAccessKey,
region: this.elasticSearchConfig.aws.region,
});
this.client = new Client({
...createAwsElasticsearchConnector(AWS.config),
node: config.node,
node: this.elasticSearchConfig.node,
});
} else if (config.node)
} else if (this.elasticSearchConfig.node)
this.client = new Client({
auth: config.auth,
node: config.node,
auth: this.elasticSearchConfig.auth,
node: this.elasticSearchConfig.node,
});
else this.logger.warn('ElasticSearch tracking is not enabled');
}
Expand All @@ -42,8 +42,7 @@ export class ElasticSearchService {
this.queue
.add(() =>
pRetry(() => this.indexRecord(index, record, params), {
retries:
this.configService.get<number>('elasticSearch.retries') ?? 3,
retries: this.elasticSearchConfig.retries,
onFailedAttempt: (error) => {
this.logger.error(
`Indexing record failed, retrying (${error.retriesLeft} attempts left)`,
Expand Down
5 changes: 4 additions & 1 deletion src/providers/geolocation/geolocation.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ConfigService } from '@nestjs/config';
import geolite2 from 'geolite2-redist';
import maxmind, { CityResponse, Reader } from 'maxmind';
import QuickLRU from 'quick-lru';
import { Configuration } from '../../config/configuration.interface';

@Injectable()
export class GeolocationService implements OnModuleDestroy {
Expand All @@ -11,7 +12,9 @@ export class GeolocationService implements OnModuleDestroy {
private lookup: Reader<CityResponse> | null = null;
private lru = new QuickLRU<string, Partial<CityResponse>>({
maxSize:
this.configService.get<number>('caching.geolocationLruSize') ?? 100,
this.configService.get<Configuration['caching']['geolocationLruSize']>(
'caching.geolocationLruSize',
) ?? 100,
});

onModuleDestroy() {
Expand Down
19 changes: 10 additions & 9 deletions src/providers/mail/mail.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,22 @@ import { MailOptions } from './mail.interface';
export class MailService {
private readonly logger = new Logger(MailService.name);
private transport: Mail;
private config: Configuration['email'];
private emailConfig = this.configService.get<Configuration['email']>('email');
private queue = new PQueue({ concurrency: 1 });
private readTemplate = mem(this.readTemplateUnmemoized);

constructor(private configService: ConfigService) {
this.config = this.configService.get<Configuration['email']>('email');
if (this.config.ses?.accessKeyId)
if (this.emailConfig.ses?.accessKeyId)
this.transport = nodemailer.createTransport({
SES: new SES({
apiVersion: '2010-12-01',
accessKeyId: this.config.ses.accessKeyId,
secretAccessKey: this.config.ses.secretAccessKey,
region: this.config.ses.region,
accessKeyId: this.emailConfig.ses.accessKeyId,
secretAccessKey: this.emailConfig.ses.secretAccessKey,
region: this.emailConfig.ses.region,
}),
} as SESTransport.Options);
else this.transport = nodemailer.createTransport(this.config.transport);
else
this.transport = nodemailer.createTransport(this.emailConfig.transport);
}

send(options: Mail.Options & MailOptions) {
Expand All @@ -43,10 +43,11 @@ export class MailService {
this.sendMail({
...options,
from:
options.from ?? `"${this.config.name}" <${this.config.from}>`,
options.from ??
`"${this.emailConfig.name}" <${this.emailConfig.from}>`,
}),
{
retries: this.configService.get<number>('email.retries') ?? 3,
retries: this.emailConfig.retries,
onFailedAttempt: (error) => {
this.logger.error(
`Mail to ${options.to} failed, retrying (${error.retriesLeft} attempts left)`,
Expand Down
Loading

0 comments on commit d2ea77b

Please sign in to comment.