Skip to content

Commit

Permalink
merge #318: AWS SES 마이그레이션 및 오류 수정
Browse files Browse the repository at this point in the history
[feat] AWS SES 마이그레이션 및 오류 수정
  • Loading branch information
yaongmeow authored Dec 9, 2023
2 parents 182a28b + 6829fb6 commit 425eadf
Show file tree
Hide file tree
Showing 12 changed files with 227 additions and 2,634 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,4 @@ jobs:
docker pull traveline/traveline-docker
docker stop traveline-container || true
docker rm traveline-container || true
docker run -e DB_HOST=${{ secrets.DB_HOST }} -e DB_PORT=${{ secrets.DB_PORT }} -e DB_USER=${{ secrets.DB_USER }} -e DB_PASSWORD=${{ secrets.DB_PASSWORD }} -e DB_DATABASE=${{ secrets.DB_DATABASE }} -e AWS_ACCESS_KEY_ID=${{ secrets.AWS_ACCESS_KEY_ID }} -e AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_ACCESS_KEY }} -e AWS_REGION=${{ secrets.AWS_REGION }} -e JWT_SECRET_ACCESS=${{ secrets.JWT_SECRET_ACCESS }} -e JWT_SECRET_REFRESH=${{ secrets.JWT_SECRET_REFRESH }} -e CLIENT_ID=${{ secrets.CLIENT_ID }} -e TEAM_ID=${{ secrets.TEAM_ID }} -e KEY_ID=${{ secrets.KEY_ID }} -e AUTH_KEY_LINE1=${{ secrets.AUTH_KEY_LINE1 }} -e AUTH_KEY_LINE2=${{ secrets.AUTH_KEY_LINE2 }} -e AUTH_KEY_LINE3=${{ secrets.AUTH_KEY_LINE3 }} -e AUTH_KEY_LINE4=${{ secrets.AUTH_KEY_LINE4 }} -e KAKAO_REST_API_KEY=${{ secrets.KAKAO_REST_API_KEY }} -e X_NCP_APIGW_API_KEY_ID=${{ secrets.X_NCP_APIGW_API_KEY_ID }} -e X_NCP_APIGW_API_KEY=${{ secrets.X_NCP_APIGW_API_KEY }} -e EMAIL_ADDRESS=${{ secrets.EMAIL_ADDRESS }} -e EMAIL_PASSWORD1=${{ secrets.EMAIL_PASSWORD1 }} -e EMAIL_PASSWORD2=${{ secrets.EMAIL_PASSWORD2 }} -e EMAIL_PASSWORD3=${{ secrets.EMAIL_PASSWORD3 }} -e EMAIL_PASSWORD4=${{ secrets.EMAIL_PASSWORD4 }} -d -p ${{secrets.EXTERNAL_PORT}}:${{secrets.INTERNAL_PORT}} --name traveline-container traveline/traveline-docker
docker run -e DB_HOST=${{ secrets.DB_HOST }} -e DB_PORT=${{ secrets.DB_PORT }} -e DB_USER=${{ secrets.DB_USER }} -e DB_PASSWORD=${{ secrets.DB_PASSWORD }} -e DB_DATABASE=${{ secrets.DB_DATABASE }} -e NCP_ACCESS_KEY_ID=${{ secrets.NCP_ACCESS_KEY_ID }} -e NCP_SECRET_ACCESS_KEY=${{ secrets.NCP_SECRET_ACCESS_KEY }} -e NCP_REGION=${{ secrets.NCP_REGION }} -e JWT_SECRET_ACCESS=${{ secrets.JWT_SECRET_ACCESS }} -e JWT_SECRET_REFRESH=${{ secrets.JWT_SECRET_REFRESH }} -e CLIENT_ID=${{ secrets.CLIENT_ID }} -e TEAM_ID=${{ secrets.TEAM_ID }} -e KEY_ID=${{ secrets.KEY_ID }} -e AUTH_KEY_LINE1=${{ secrets.AUTH_KEY_LINE1 }} -e AUTH_KEY_LINE2=${{ secrets.AUTH_KEY_LINE2 }} -e AUTH_KEY_LINE3=${{ secrets.AUTH_KEY_LINE3 }} -e AUTH_KEY_LINE4=${{ secrets.AUTH_KEY_LINE4 }} -e KAKAO_REST_API_KEY=${{ secrets.KAKAO_REST_API_KEY }} -e X_NCP_APIGW_API_KEY_ID=${{ secrets.X_NCP_APIGW_API_KEY_ID }} -e X_NCP_APIGW_API_KEY=${{ secrets.X_NCP_APIGW_API_KEY }} -e AWS_ACCESS_KEY_ID=${{ secrets.AWS_ACCESS_KEY_ID }} -e AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_ACCESS_KEY }} -e AWS_REGION=${{ secrets.AWS_REGION }} -d -p ${{secrets.EXTERNAL_PORT}}:${{secrets.INTERNAL_PORT}} --name traveline-container traveline/traveline-docker
2,689 changes: 128 additions & 2,561 deletions BE/package-lock.json

Large diffs are not rendered by default.

2 changes: 0 additions & 2 deletions BE/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@nestjs-modules/mailer": "^1.9.1",
"@nestjs/axios": "^3.0.1",
"@nestjs/common": "^10.0.0",
"@nestjs/config": "^3.1.1",
Expand All @@ -39,7 +38,6 @@
"jwks-rsa": "^3.1.0",
"mysql2": "^3.6.3",
"nest-winston": "^1.9.4",
"nodemailer": "^6.9.7",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.8.1",
"typeorm": "^0.3.17",
Expand Down
34 changes: 2 additions & 32 deletions BE/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ import { ConfigModule, ConfigService } from '@nestjs/config';
import { DatabaseModule } from './database/database.module';
import { StorageModule } from './storage/storage.module';
import { JwtModule } from '@nestjs/jwt';
import { MailerModule } from '@nestjs-modules/mailer';
import { EjsAdapter } from '@nestjs-modules/mailer/dist/adapters/ejs.adapter';
import { EmailModule } from './email/email.module';

@Module({
imports: [
Expand All @@ -23,42 +22,13 @@ import { EjsAdapter } from '@nestjs-modules/mailer/dist/adapters/ejs.adapter';
secret: process.env.JWT_SECRET_ACCESS,
signOptions: { expiresIn: '30d' },
}),
MailerModule.forRootAsync({
useFactory: () => ({
transport: {
host: 'smtp.gmail.com',
port: 587,
auth: {
user: process.env.EMAIL_ADDRESS,
pass:
process.env.EMAIL_PASSWORD1 +
' ' +
process.env.EMAIL_PASSWORD2 +
' ' +
process.env.EMAIL_PASSWORD3 +
' ' +
process.env.EMAIL_PASSWORD4,
},
},
defaults: {
from: `"no-reply" <${process.env.EMAIL_ADDRESS}>`,
},
preview: true,
template: {
dir: __dirname + '/../views/',
adapter: new EjsAdapter(),
options: {
strict: true,
},
},
}),
}),
StorageModule,
UsersModule,
PostingsModule,
TimelinesModule,
AuthModule,
DatabaseModule,
EmailModule,
],
controllers: [AppController],
providers: [AppService, ConfigService],
Expand Down
6 changes: 1 addition & 5 deletions BE/src/app.service.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
import { MailerService } from '@nestjs-modules/mailer';
import { StorageService } from './storage/storage.service';
import { Injectable } from '@nestjs/common';

@Injectable()
export class AppService {
constructor(
private readonly storageService: StorageService,
private readonly mailerService: MailerService
) {}
constructor(private readonly storageService: StorageService) {}

getHello(): string {
return `
Expand Down
7 changes: 2 additions & 5 deletions BE/src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import { CreateAuthRequestForDevDto } from './dto/create-auth-request-for-dev.dt
import { DeleteAuthDto } from './dto/delete-auth.dto';
import { AuthGuard } from './auth.guard';
import { login, refresh, withdrawal } from './auth.swagger';
import * as ip from 'ip';

@Controller('auth')
@ApiTags('Auth API')
Expand All @@ -31,8 +30,7 @@ export class AuthController {
})
@ApiOkResponse({ description: 'OK', schema: { example: refresh } })
refresh(@Req() request) {
const ipAddress = ip.address();
return this.authService.refresh(request, ipAddress);
return this.authService.refresh(request);
}

@Post('login')
Expand All @@ -43,8 +41,7 @@ export class AuthController {
})
@ApiOkResponse({ description: 'OK', schema: { example: login } })
login(@Req() request, @Body() createAuthDto: CreateAuthRequestDto) {
const ipAddress = ip.address();
return this.authService.login(request, createAuthDto, ipAddress);
return this.authService.login(request, createAuthDto);
}

@Post('login/dev')
Expand Down
3 changes: 2 additions & 1 deletion BE/src/auth/auth.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import { UsersService } from 'src/users/users.service';
import { StorageService } from 'src/storage/storage.service';
import { HttpModule } from '@nestjs/axios';
import { AuthGuard } from './auth.guard';
import { EmailModule } from 'src/email/email.module';

@Module({
imports: [UsersModule, HttpModule],
imports: [UsersModule, HttpModule, EmailModule],
controllers: [AuthController],
providers: [AuthService, UsersService, StorageService, AuthGuard],
})
Expand Down
56 changes: 33 additions & 23 deletions BE/src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,19 @@ import { CreateAuthRequestDto } from './dto/create-auth-request.dto';
import { CreateAuthRequestForDevDto } from './dto/create-auth-request-for-dev.dto';
import { firstValueFrom } from 'rxjs';
import { JwksClient } from 'jwks-rsa';
import { MailerService } from '@nestjs-modules/mailer';
import { EmailService } from 'src/email/email.service';

@Injectable()
export class AuthService {
constructor(
private readonly jwtService: JwtService,
private readonly httpService: HttpService,
private readonly usersService: UsersService,
private readonly mailerService: MailerService
private readonly emilService: EmailService
) {}

async refresh(request, ipAddress) {
async refresh(request) {
const ipAddress = request.headers['x-real-ip'];
const [type, token] = request.headers.authorization?.split(' ') ?? [];
if (type !== 'Bearer') {
throw new BadRequestException('JWT가 아닙니다.');
Expand Down Expand Up @@ -93,7 +94,8 @@ export class AuthService {
return decodedIdToken;
}

async login(request, createAuthDto: CreateAuthRequestDto, ipAddress: string) {
async login(request, createAuthDto: CreateAuthRequestDto) {
const ipAddress = request.headers['x-real-ip'];
const [type, token] = request.headers.authorization?.split(' ') ?? [];
if (type === 'Bearer' && token) {
throw new BadRequestException('JWT가 이미 존재합니다.');
Expand Down Expand Up @@ -127,16 +129,17 @@ export class AuthService {
);
}
if (!(ipAddress in allowedIpArray)) {
await this.mailerService.sendMail({
to: user.email,
subject: '[traveline] 새로운 환경 로그인 안내',
template: '../views/email',
context: {
username: user.name,
id: user.id,
newIp: ipAddress,
},
const html = await this.emilService.template('email.ejs', {
username: user.name,
newIp: ipAddress,
id: user.id,
});

await this.emilService.sendEmail(
user.email,
'[traveline] 새로운 환경 로그인 안내',
html
);
}
}

Expand All @@ -158,17 +161,18 @@ export class AuthService {
if (user) {
const payload = { id };
try {
await this.mailerService.sendMail({
to: user.email,
subject: '[traveline] 새로운 환경 로그인 안내',
template: '../views/email',
context: {
username: user.name,
id,
newIp: '아이피',
},
const html = await this.emilService.template('email.ejs', {
username: user.name,
newIp: '아 이 피',
id: user.id,
});

await this.emilService.sendEmail(
user.email,
'[traveline] 새로운 환경 로그인 안내',
html
);

return {
accessToken: await this.jwtService.signAsync(payload),
refreshToken: await this.jwtService.signAsync(payload, {
Expand Down Expand Up @@ -272,7 +276,13 @@ export class AuthService {
const user = await this.usersService.findUserById(id);

const allowedIp = user.allowedIp;
const bannedIp = user.bannedIp;

let bannedIp;
if (user.bannedIp === null) {
bannedIp = [];
} else {
bannedIp = user.bannedIp;
}

if (allow) {
allowedIp.push(ip);
Expand Down
5 changes: 5 additions & 0 deletions BE/src/email/email.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Module } from '@nestjs/common';
import { EmailService } from './email.service';

@Module({ providers: [EmailService], exports: [EmailService] })
export class EmailModule {}
49 changes: 49 additions & 0 deletions BE/src/email/email.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import * as AWS from 'aws-sdk';
import * as ejs from 'ejs';

export class EmailService {
private readonly ses: AWS.SES = new AWS.SES({
region: process.env.AWS_REGION,
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
},
});

async sendEmail(email: string, title: string, html: string) {
const sendEmailParams: AWS.SES.SendEmailRequest = {
Destination: {
CcAddresses: [],
ToAddresses: [email], // 받을 사람의 이메일
},
Message: {
Body: {
Html: {
Charset: 'UTF-8',
Data: html,
},
},
Subject: {
Charset: 'UTF-8',
Data: title,
},
},
Source: '[email protected]',
ReplyToAddresses: [],
};
const result = await this.ses.sendEmail(sendEmailParams).promise();
return result;
}

async template(ejsFileName: string, data: object) {
const path = __dirname + '/../../views/' + ejsFileName; // ejs 파일 경로

let html;
await ejs.renderFile(path, data, async (err, res) => {
if (!err) {
html = res;
}
});
return html;
}
}
4 changes: 2 additions & 2 deletions BE/src/storage/storage.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ export class StorageService {
endpoint: 'https://kr.object.ncloudstorage.com',
region: process.env.AWS_REGION,
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
accessKeyId: process.env.NCP_ACCESS_KEY_ID,
secretAccessKey: process.env.NCP_SECRET_ACCESS_KEY,
},
});
private readonly bucketName = 'traveline';
Expand Down
4 changes: 2 additions & 2 deletions BE/views/email.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@
<div>
본인일 경우
<a
href="http://localhost:3000/auth/ip?id=<%= locals.id %>&ip=<%= locals.newIp %>&allow=true"
href="https://traveline.store/auth/ip?id=<%= locals.id %>&ip=<%= locals.newIp %>&allow=true"
>여기</a
>를, 아닐 경우
<a
href="http://localhost:3000/auth/ip?id=<%= locals.id %>&ip=<%= locals.newIp %>&allow=flase"
href="https://traveline.store/auth/ip?id=<%= locals.id %>&ip=<%= locals.newIp %>&allow=flase"
>여기</a
>를 클릭해주세요.
</div>
Expand Down

0 comments on commit 425eadf

Please sign in to comment.