feat: nestjs config package added

This commit is contained in:
Mir Arif Hasan
2023-11-24 16:13:59 +06:00
parent 49c2ee0d38
commit 5bee3471c5
13 changed files with 798 additions and 551 deletions

View File

@@ -28,6 +28,7 @@
"@nestjs-modules/mailer": "^1.9.1",
"@nestjs/apollo": "^12.0.9",
"@nestjs/common": "^10.2.6",
"@nestjs/config": "^3.1.1",
"@nestjs/core": "^10.2.6",
"@nestjs/graphql": "^12.0.9",
"@nestjs/jwt": "^10.1.1",

View File

@@ -25,6 +25,7 @@ import { TeamEnvironmentsService } from '../team-environments/team-environments.
import { TeamInvitationService } from '../team-invitation/team-invitation.service';
import { TeamMemberRole } from '../team/team.model';
import { ShortcodeService } from 'src/shortcode/shortcode.service';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class AdminService {
@@ -39,6 +40,7 @@ export class AdminService {
private readonly prisma: PrismaService,
private readonly mailerService: MailerService,
private readonly shortcodeService: ShortcodeService,
private readonly configService: ConfigService,
) {}
/**
@@ -79,7 +81,7 @@ export class AdminService {
template: 'user-invitation',
variables: {
inviteeEmail: inviteeEmail,
magicLink: `${process.env.VITE_BASE_URL}`,
magicLink: `${this.configService.get('VITE_BASE_URL')}`,
},
});
} catch (e) {

View File

@@ -20,49 +20,62 @@ import { ShortcodeModule } from './shortcode/shortcode.module';
import { COOKIES_NOT_FOUND } from './errors';
import { ThrottlerModule } from '@nestjs/throttler';
import { AppController } from './app.controller';
import { ConfigModule, ConfigService } from '@nestjs/config';
@Module({
imports: [
GraphQLModule.forRoot<ApolloDriverConfig>({
buildSchemaOptions: {
numberScalarMode: 'integer',
},
playground: process.env.PRODUCTION !== 'true',
autoSchemaFile: true,
installSubscriptionHandlers: true,
subscriptions: {
'subscriptions-transport-ws': {
path: '/graphql',
onConnect: (_, websocket) => {
try {
const cookies = subscriptionContextCookieParser(
websocket.upgradeReq.headers.cookie,
);
return {
headers: { ...websocket?.upgradeReq?.headers, cookies },
};
} catch (error) {
throw new HttpException(COOKIES_NOT_FOUND, 400, {
cause: new Error(COOKIES_NOT_FOUND),
});
}
},
},
},
context: ({ req, res, connection }) => ({
req,
res,
connection,
}),
driver: ApolloDriver,
ConfigModule.forRoot({
isGlobal: true,
}),
ThrottlerModule.forRoot([
{
ttl: +process.env.RATE_LIMIT_TTL,
limit: +process.env.RATE_LIMIT_MAX,
GraphQLModule.forRootAsync<ApolloDriverConfig>({
driver: ApolloDriver,
imports: [ConfigModule],
inject: [ConfigService],
useFactory: async (configService: ConfigService) => {
return {
buildSchemaOptions: {
numberScalarMode: 'integer',
},
playground: configService.get('PRODUCTION') !== 'true',
autoSchemaFile: true,
installSubscriptionHandlers: true,
subscriptions: {
'subscriptions-transport-ws': {
path: '/graphql',
onConnect: (_, websocket) => {
try {
const cookies = subscriptionContextCookieParser(
websocket.upgradeReq.headers.cookie,
);
return {
headers: { ...websocket?.upgradeReq?.headers, cookies },
};
} catch (error) {
throw new HttpException(COOKIES_NOT_FOUND, 400, {
cause: new Error(COOKIES_NOT_FOUND),
});
}
},
},
},
context: ({ req, res, connection }) => ({
req,
res,
connection,
}),
};
},
]),
}),
ThrottlerModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: async (configService: ConfigService) => [
{
ttl: +configService.get('RATE_LIMIT_TTL'),
limit: +configService.get('RATE_LIMIT_MAX'),
},
],
}),
UserModule,
AuthModule,
AdminModule,

View File

@@ -12,6 +12,7 @@ import { GoogleStrategy } from './strategies/google.strategy';
import { GithubStrategy } from './strategies/github.strategy';
import { MicrosoftStrategy } from './strategies/microsoft.strategy';
import { AuthProvider, authProviderCheck } from './helper';
import { ConfigModule, ConfigService } from '@nestjs/config';
@Module({
imports: [
@@ -19,8 +20,12 @@ import { AuthProvider, authProviderCheck } from './helper';
UserModule,
MailerModule,
PassportModule,
JwtModule.register({
secret: process.env.JWT_SECRET,
JwtModule.registerAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: async (configService: ConfigService) => ({
secret: configService.get('JWT_SECRET'),
}),
}),
],
providers: [

View File

@@ -28,6 +28,7 @@ import { AuthError } from 'src/types/AuthError';
import { AuthUser, IsAdmin } from 'src/types/AuthUser';
import { VerificationToken } from '@prisma/client';
import { Origin } from './helper';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class AuthService {
@@ -36,6 +37,7 @@ export class AuthService {
private prismaService: PrismaService,
private jwtService: JwtService,
private readonly mailerService: MailerService,
private readonly configService: ConfigService,
) {}
/**
@@ -46,10 +48,12 @@ export class AuthService {
*/
private async generateMagicLinkTokens(user: AuthUser) {
const salt = await bcrypt.genSalt(
parseInt(process.env.TOKEN_SALT_COMPLEXITY),
parseInt(this.configService.get('TOKEN_SALT_COMPLEXITY')),
);
const expiresOn = DateTime.now()
.plus({ hours: parseInt(process.env.MAGIC_LINK_TOKEN_VALIDITY) })
.plus({
hours: parseInt(this.configService.get('MAGIC_LINK_TOKEN_VALIDITY')),
})
.toISO()
.toString();
@@ -95,13 +99,13 @@ export class AuthService {
*/
private async generateRefreshToken(userUid: string) {
const refreshTokenPayload: RefreshTokenPayload = {
iss: process.env.VITE_BASE_URL,
iss: this.configService.get('VITE_BASE_URL'),
sub: userUid,
aud: [process.env.VITE_BASE_URL],
aud: [this.configService.get('VITE_BASE_URL')],
};
const refreshToken = await this.jwtService.sign(refreshTokenPayload, {
expiresIn: process.env.REFRESH_TOKEN_VALIDITY, //7 Days
expiresIn: this.configService.get('REFRESH_TOKEN_VALIDITY'), //7 Days
});
const refreshTokenHash = await argon2.hash(refreshToken);
@@ -127,9 +131,9 @@ export class AuthService {
*/
async generateAuthTokens(userUid: string) {
const accessTokenPayload: AccessTokenPayload = {
iss: process.env.VITE_BASE_URL,
iss: this.configService.get('VITE_BASE_URL'),
sub: userUid,
aud: [process.env.VITE_BASE_URL],
aud: [this.configService.get('VITE_BASE_URL')],
};
const refreshToken = await this.generateRefreshToken(userUid);
@@ -137,7 +141,7 @@ export class AuthService {
return E.right(<AuthTokens>{
access_token: await this.jwtService.sign(accessTokenPayload, {
expiresIn: process.env.ACCESS_TOKEN_VALIDITY, //1 Day
expiresIn: this.configService.get('ACCESS_TOKEN_VALIDITY'), //1 Day
}),
refresh_token: refreshToken.right,
});
@@ -218,14 +222,14 @@ export class AuthService {
let url: string;
switch (origin) {
case Origin.ADMIN:
url = process.env.VITE_ADMIN_URL;
url = this.configService.get('VITE_ADMIN_URL');
break;
case Origin.APP:
url = process.env.VITE_BASE_URL;
url = this.configService.get('VITE_BASE_URL');
break;
default:
// if origin is invalid by default set URL to Hoppscotch-App
url = process.env.VITE_BASE_URL;
url = this.configService.get('VITE_BASE_URL');
}
await this.mailerService.sendEmail(email, {

View File

@@ -5,18 +5,20 @@ import { AuthService } from '../auth.service';
import { UserService } from 'src/user/user.service';
import * as O from 'fp-ts/Option';
import * as E from 'fp-ts/Either';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class GithubStrategy extends PassportStrategy(Strategy) {
constructor(
private authService: AuthService,
private usersService: UserService,
private configService: ConfigService,
) {
super({
clientID: process.env.GITHUB_CLIENT_ID,
clientSecret: process.env.GITHUB_CLIENT_SECRET,
callbackURL: process.env.GITHUB_CALLBACK_URL,
scope: [process.env.GITHUB_SCOPE],
clientID: configService.get('GITHUB_CLIENT_ID'),
clientSecret: configService.get('GITHUB_CLIENT_SECRET'),
callbackURL: configService.get('GITHUB_CALLBACK_URL'),
scope: [configService.get('GITHUB_SCOPE')],
store: true,
});
}

View File

@@ -5,18 +5,20 @@ import { UserService } from 'src/user/user.service';
import * as O from 'fp-ts/Option';
import { AuthService } from '../auth.service';
import * as E from 'fp-ts/Either';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class GoogleStrategy extends PassportStrategy(Strategy) {
constructor(
private usersService: UserService,
private authService: AuthService,
private configService: ConfigService,
) {
super({
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: process.env.GOOGLE_CALLBACK_URL,
scope: process.env.GOOGLE_SCOPE.split(','),
clientID: configService.get('GOOGLE_CLIENT_ID'),
clientSecret: configService.get('GOOGLE_CLIENT_SECRET'),
callbackURL: configService.get('GOOGLE_CALLBACK_URL'),
scope: configService.get('GOOGLE_SCOPE').split(','),
passReqToCallback: true,
store: true,
});

View File

@@ -15,10 +15,14 @@ import {
INVALID_ACCESS_TOKEN,
USER_NOT_FOUND,
} from 'src/errors';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
constructor(private usersService: UserService) {
constructor(
private usersService: UserService,
private configService: ConfigService,
) {
super({
jwtFromRequest: ExtractJwt.fromExtractors([
(request: Request) => {
@@ -29,7 +33,7 @@ export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
return ATCookie;
},
]),
secretOrKey: process.env.JWT_SECRET,
secretOrKey: configService.get('JWT_SECRET'),
});
}

View File

@@ -5,19 +5,21 @@ import { AuthService } from '../auth.service';
import { UserService } from 'src/user/user.service';
import * as O from 'fp-ts/Option';
import * as E from 'fp-ts/Either';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class MicrosoftStrategy extends PassportStrategy(Strategy) {
constructor(
private authService: AuthService,
private usersService: UserService,
private configService: ConfigService,
) {
super({
clientID: process.env.MICROSOFT_CLIENT_ID,
clientSecret: process.env.MICROSOFT_CLIENT_SECRET,
callbackURL: process.env.MICROSOFT_CALLBACK_URL,
scope: [process.env.MICROSOFT_SCOPE],
tenant: process.env.MICROSOFT_TENANT,
clientID: configService.get('MICROSOFT_CLIENT_ID'),
clientSecret: configService.get('MICROSOFT_CLIENT_SECRET'),
callbackURL: configService.get('MICROSOFT_CALLBACK_URL'),
scope: [configService.get('MICROSOFT_SCOPE')],
tenant: configService.get('MICROSOFT_TENANT'),
store: true,
});
}

View File

@@ -14,10 +14,14 @@ import {
USER_NOT_FOUND,
} from 'src/errors';
import * as O from 'fp-ts/Option';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class RTJwtStrategy extends PassportStrategy(Strategy, 'jwt-refresh') {
constructor(private usersService: UserService) {
constructor(
private usersService: UserService,
private configService: ConfigService,
) {
super({
jwtFromRequest: ExtractJwt.fromExtractors([
(request: Request) => {
@@ -28,7 +32,7 @@ export class RTJwtStrategy extends PassportStrategy(Strategy, 'jwt-refresh') {
return RTCookie;
},
]),
secretOrKey: process.env.JWT_SECRET,
secretOrKey: configService.get('JWT_SECRET'),
});
}

View File

@@ -7,21 +7,27 @@ import {
MAILER_FROM_ADDRESS_UNDEFINED,
MAILER_SMTP_URL_UNDEFINED,
} from 'src/errors';
import { ConfigModule, ConfigService } from '@nestjs/config';
@Module({
imports: [
NestMailerModule.forRoot({
transport:
process.env.MAILER_SMTP_URL ?? throwErr(MAILER_SMTP_URL_UNDEFINED),
defaults: {
from:
process.env.MAILER_ADDRESS_FROM ??
throwErr(MAILER_FROM_ADDRESS_UNDEFINED),
},
template: {
dir: __dirname + '/templates',
adapter: new HandlebarsAdapter(),
},
NestMailerModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: async (configService: ConfigService) => ({
transport:
configService.get('MAILER_SMTP_URL') ??
throwErr(MAILER_SMTP_URL_UNDEFINED),
defaults: {
from:
configService.get('MAILER_ADDRESS_FROM') ??
throwErr(MAILER_FROM_ADDRESS_UNDEFINED),
},
template: {
dir: __dirname + '/templates',
adapter: new HandlebarsAdapter(),
},
}),
}),
],
providers: [MailerService],

View File

@@ -20,6 +20,7 @@ import { UserService } from 'src/user/user.service';
import { PubSubService } from 'src/pubsub/pubsub.service';
import { validateEmail } from '../utils';
import { AuthUser } from 'src/types/AuthUser';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class TeamInvitationService {
@@ -28,8 +29,8 @@ export class TeamInvitationService {
private readonly userService: UserService,
private readonly teamService: TeamService,
private readonly mailerService: MailerService,
private readonly pubsub: PubSubService,
private readonly configService: ConfigService,
) {}
/**
@@ -150,7 +151,9 @@ export class TeamInvitationService {
template: 'team-invitation',
variables: {
invitee: creator.displayName ?? 'A Hoppscotch User',
action_url: `${process.env.VITE_BASE_URL}/join-team?id=${dbInvitation.id}`,
action_url: `${this.configService.get('VITE_BASE_URL')}/join-team?id=${
dbInvitation.id
}`,
invite_team_name: team.name,
},
});