Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] AWS SES 마이그레이션 및 오류 수정 #318

Merged
merged 7 commits into from
Dec 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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