Skip to content

Commit 1ed51ba

Browse files
authored
Merge pull request #388 from Quickchive/develop
Develop to master
2 parents 174104e + 0ba0126 commit 1ed51ba

File tree

6 files changed

+110
-240
lines changed

6 files changed

+110
-240
lines changed

src/auth/oauth.service.ts

Lines changed: 70 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { RedisService } from '../infra/redis/redis.service';
2121
import { REFRESH_TOKEN_KEY } from './constants';
2222
import { KakaoLoginRequest } from './dtos/request/kakao-login.request.dto';
2323
import { KakaoLoginDto } from './dtos/kakao-login.dto';
24+
import { PROVIDER } from '../users/constant/provider.constant';
2425

2526
@Injectable()
2627
export class OAuthService {
@@ -97,61 +98,56 @@ export class OAuthService {
9798
throw new BadRequestException('Please Agree to share your email');
9899
}
99100

100-
let user = await this.userRepository.findOneByEmail(email);
101+
const user = await this.userRepository.findOneByEmail(email);
102+
if (user) {
103+
return this.oauthLogin(user.email);
104+
}
101105

102106
// 회원가입인 경우 기본 카테고리 생성 작업 진행
103-
if (!user) {
104-
user = new User();
105-
user.email = email;
106-
user.name = userInfo.kakao_account.profile.nickname;
107-
user.profileImage = userInfo.kakao_account.profile?.profile_image_url;
108-
user.password = this.encodePasswordFromEmail(
109-
email,
110-
process.env.KAKAO_JS_KEY,
111-
);
107+
const newUser = User.of({
108+
email,
109+
name: userInfo.kakao_account.profile.nickname,
110+
profileImage: userInfo.kakao_account.profile?.profile_image_url,
111+
password: this.encodePasswordFromEmail(email, process.env.KAKAO_JS_KEY),
112+
provider: PROVIDER.KAKAO,
113+
});
112114

113-
await this.userRepository.createOne(user);
114-
await this.categoryRepository.createDefaultCategories(user);
115-
}
115+
await this.userRepository.createOne(newUser);
116+
await this.categoryRepository.createDefaultCategories(newUser);
116117

117-
return this.oauthLogin(user.email);
118+
return this.oauthLogin(newUser.email);
118119
} catch (e) {
119120
throw e;
120121
}
121122
}
122123

123124
async createOneWithKakao({ authorizationToken }: KakaoLoginDto) {
124-
try {
125-
const { userInfo } = await this.oauthUtil.getKakaoUserInfo(
126-
authorizationToken,
127-
);
125+
const { userInfo } =
126+
await this.oauthUtil.getKakaoUserInfo(authorizationToken);
128127

129-
const email = userInfo.kakao_account.email;
130-
if (!email) {
131-
throw new BadRequestException('Please Agree to share your email');
132-
}
133-
134-
let user = await this.userRepository.findOneByEmail(email);
135-
136-
// 회원가입인 경우 기본 카테고리 생성 작업 진행
137-
if (!user) {
138-
user = new User();
139-
user.email = email;
140-
user.name = userInfo.kakao_account.profile.nickname;
141-
user.profileImage = userInfo.kakao_account.profile?.profile_image_url;
142-
user.password = this.encodePasswordFromEmail(
143-
email,
144-
process.env.KAKAO_JS_KEY,
145-
);
128+
const email = userInfo.kakao_account.email;
129+
if (!email) {
130+
throw new BadRequestException('Please Agree to share your email');
131+
}
146132

147-
await this.userRepository.createOne(user);
148-
await this.categoryRepository.createDefaultCategories(user);
149-
}
133+
const user = await this.userRepository.findOneByEmail(email);
150134

135+
if (user) {
151136
return this.oauthLogin(user.email);
152-
} catch (e) {
153-
throw e;
154137
}
138+
139+
// 회원가입인 경우 기본 카테고리 생성 작업 진행
140+
const newUser = User.of({
141+
email,
142+
name: userInfo.kakao_account.profile.nickname,
143+
profileImage: userInfo.kakao_account.profile?.profile_image_url,
144+
password: this.encodePasswordFromEmail(email, process.env.KAKAO_JS_KEY),
145+
provider: PROVIDER.KAKAO,
146+
});
147+
148+
await this.userRepository.createOne(newUser);
149+
await this.categoryRepository.createDefaultCategories(newUser);
150+
return this.oauthLogin(newUser.email);
155151
}
156152

157153
// Login with Google account info
@@ -160,28 +156,28 @@ export class OAuthService {
160156
name,
161157
picture,
162158
}: googleUserInfo): Promise<LoginOutput> {
163-
try {
164-
let user = await this.userRepository.findOneByEmail(email);
165-
166-
// 회원가입인 경우 기본 카테고리 생성 작업 진행
167-
if (!user) {
168-
user = new User();
169-
user.email = email;
170-
user.name = name;
171-
user.profileImage = picture;
172-
user.password = this.encodePasswordFromEmail(
173-
email,
174-
process.env.GOOGLE_CLIENT_ID,
175-
);
176-
177-
await this.userRepository.createOne(user);
178-
await this.categoryRepository.createDefaultCategories(user);
179-
}
159+
const user = await this.userRepository.findOneByEmail(email);
180160

161+
if (user) {
181162
return this.oauthLogin(user.email);
182-
} catch (e) {
183-
throw e;
184163
}
164+
165+
// 회원가입인 경우 기본 카테고리 생성 작업 진행
166+
const newUser = User.of({
167+
email,
168+
name,
169+
profileImage: picture,
170+
password: this.encodePasswordFromEmail(
171+
email,
172+
process.env.GOOGLE_CLIENT_ID,
173+
),
174+
provider: PROVIDER.GOOGLE,
175+
});
176+
177+
await this.userRepository.createOne(newUser);
178+
await this.categoryRepository.createDefaultCategories(newUser);
179+
180+
return this.oauthLogin(newUser.email);
185181
}
186182

187183
private encodePasswordFromEmail(email: string, key?: string): string {
@@ -229,21 +225,25 @@ export class OAuthService {
229225

230226
const { sub: id, email } = this.jwtService.decode(data.id_token);
231227

232-
let user = await this.userRepository.findOneByEmail(email);
228+
const user = await this.userRepository.findOneByEmail(email);
233229

234-
if (!user) {
235-
user = new User();
236-
user.email = email;
237-
user.name = email.split('@')[0];
238-
user.password = this.encodePasswordFromEmail(
230+
if (user) {
231+
return this.oauthLogin(user.email);
232+
}
233+
234+
const newUser = User.of({
235+
email,
236+
name: email.split('@')[0],
237+
password: this.encodePasswordFromEmail(
239238
email,
240239
process.env.APPLE_CLIENT_ID,
241-
);
240+
),
241+
provider: PROVIDER.APPLE,
242+
});
242243

243-
await this.userRepository.createOne(user);
244-
await this.categoryRepository.createDefaultCategories(user);
245-
}
244+
await this.userRepository.createOne(newUser);
245+
await this.categoryRepository.createDefaultCategories(newUser);
246246

247-
return this.oauthLogin(user.email);
247+
return this.oauthLogin(newUser.email);
248248
}
249249
}

src/categories/category.controller.ts

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -157,19 +157,4 @@ export class CategoryController {
157157
): Promise<LoadFrequentCategoriesOutput> {
158158
return this.categoryService.loadFrequentCategories(user);
159159
}
160-
161-
@ApiOperation({
162-
summary: '아티클 카테고리 자동 지정',
163-
description:
164-
'아티클에 적절한 카테고리를 유저의 카테고리 목록에서 찾는 메서드',
165-
})
166-
@ApiBearerAuth('Authorization')
167-
@UseGuards(JwtAuthGuard)
168-
@Get('auto-categorize')
169-
async autoCategorize(
170-
@AuthUser() user: User,
171-
@Query() { link }: AutoCategorizeRequest,
172-
): Promise<AutoCategorizeOutput> {
173-
return this.categoryService.autoCategorize(user, link);
174-
}
175160
}

src/categories/category.service.ts

Lines changed: 1 addition & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -414,79 +414,6 @@ export class CategoryService {
414414
}
415415
}
416416

417-
async autoCategorize(
418-
user: User,
419-
link: string,
420-
): Promise<AutoCategorizeOutput> {
421-
try {
422-
const userInDb = await this.userRepository.findOneWithCategories(user.id);
423-
if (!userInDb) {
424-
throw new NotFoundException('User not found');
425-
}
426-
427-
if (!userInDb.categories) {
428-
throw new NotFoundException('Categories not found');
429-
}
430-
const categories: string[] = [];
431-
userInDb.categories.forEach((category) => {
432-
if (!category.parentId) {
433-
categories.push(category.name);
434-
}
435-
});
436-
const { title, siteName, description } = await getLinkInfo(link);
437-
438-
const content = await getLinkContent(link);
439-
440-
const questionLines = [
441-
"You are a machine tasked with auto-categorizing articles based on information obtained through web scraping. You can only answer a single category name. Here is the article's information:",
442-
];
443-
444-
if (title) {
445-
questionLines.push(
446-
`The article in question is titled "${title.trim()}"`,
447-
);
448-
}
449-
450-
if (content) {
451-
const contentLength = content.length / 2;
452-
questionLines.push(
453-
`The 150 characters of the article is, "${content
454-
.replace(/\s/g, '')
455-
.slice(contentLength - 150, contentLength + 150)
456-
.trim()}"`,
457-
);
458-
}
459-
460-
if (description) {
461-
questionLines.push(`The description is ${description.trim()}"`);
462-
}
463-
464-
if (siteName) {
465-
questionLines.push(`The site's name is "${siteName.trim()}"`);
466-
}
467-
468-
// Add the category options to the end of the list
469-
questionLines.push(
470-
`Please provide the most suitable category among the following. Here is Category options: [${categories.join(
471-
', ',
472-
)}, None]`,
473-
);
474-
475-
// Join all lines together into a single string
476-
const question = questionLines.join(' ');
477-
console.log(question);
478-
479-
const response = await this.openaiService.createChatCompletion({
480-
question,
481-
temperature: 0,
482-
});
483-
484-
return { category: response.choices[0].message?.content || 'None' };
485-
} catch (e) {
486-
throw e;
487-
}
488-
}
489-
490417
async autoCategorizeWithId(user: User, link: string) {
491418
const _categories = await this.categoryRepository.findByUserId(user.id);
492419
if (_categories.length === 0) {
@@ -539,6 +466,7 @@ Given the categories below, please provide suitable category for the article fol
539466
540467
541468
Present your reply options in JSON format below.
469+
- If there's no suitable category, must provide reply with "None".
542470
\`\`\`json
543471
{
544472
"id": id,
@@ -569,66 +497,4 @@ Present your reply options in JSON format below.
569497
throw e;
570498
}
571499
}
572-
573-
async autoCategorizeForTest(
574-
autoCategorizeBody: AutoCategorizeBodyDto,
575-
): Promise<AutoCategorizeOutput> {
576-
try {
577-
const { link, categories } = autoCategorizeBody;
578-
const { title, siteName, description } = await getLinkInfo(link);
579-
580-
/**
581-
* TODO: 본문 크롤링 개선 필요
582-
* 현재 p 태그만 크롤링하는데, 불필요한 내용이 포함되는 경우가 많음
583-
* 그러나 하나하나 예외 처리하는 방법을 제외하곤 방법을 못 찾은 상황
584-
*/
585-
const content = await getLinkContent(link);
586-
587-
const questionLines = [
588-
"You are a machine tasked with auto-categorizing articles based on information obtained through web scraping. You can only answer a single category name. Here is the article's information:",
589-
];
590-
591-
if (title) {
592-
questionLines.push(
593-
`The article in question is titled "${title.trim()}"`,
594-
);
595-
}
596-
597-
if (content) {
598-
const contentLength = content.length / 2;
599-
questionLines.push(
600-
`The 150 characters of the article is, "${content
601-
.replace(/\s/g, '')
602-
.slice(contentLength - 150, contentLength + 150)
603-
.trim()}"`,
604-
);
605-
}
606-
607-
if (description) {
608-
questionLines.push(`The description is ${description.trim()}"`);
609-
}
610-
611-
if (siteName) {
612-
questionLines.push(`The site's name is "${siteName.trim()}"`);
613-
}
614-
615-
// Add the category options to the end of the list
616-
questionLines.push(
617-
`Please provide the most suitable category among the following. Here is Category options: [${categories.join(
618-
', ',
619-
)}, None]`,
620-
);
621-
622-
// Join all lines together into a single string
623-
const question = questionLines.join(' ');
624-
625-
const response = await this.openaiService.createChatCompletion({
626-
question,
627-
});
628-
629-
return { category: response.choices[0].message?.content || 'None' };
630-
} catch (e) {
631-
throw e;
632-
}
633-
}
634500
}

src/test/test.controller.ts

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,6 @@ import {
77
} from '@nestjs/swagger';
88
import { ErrorOutput } from '../common/dtos/output.dto';
99
import { ContentsService } from '../contents/contents.service';
10-
import {
11-
AutoCategorizeBodyDto,
12-
AutoCategorizeOutput,
13-
} from '../categories/dtos/category.dto';
1410
import {
1511
SummarizeContentOutput,
1612
SummarizeContentBodyDto,
@@ -43,15 +39,4 @@ export class TestController {
4339
): Promise<SummarizeContentOutput> {
4440
return this.contentsService.testSummarizeContent(content);
4541
}
46-
47-
@ApiOperation({
48-
summary: '아티클 카테고리 자동 지정 (테스트용)',
49-
description: 'url을 넘기면 적절한 아티클 카테고리를 반환하는 메서드',
50-
})
51-
@Post('auto-categorize')
52-
async autoCategorize(
53-
@Body() autoCategorizeBody: AutoCategorizeBodyDto,
54-
): Promise<AutoCategorizeOutput> {
55-
return this.categoryService.autoCategorizeForTest(autoCategorizeBody);
56-
}
5742
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export const PROVIDER = {
2+
GOOGLE: 'google',
3+
KAKAO: 'kakao',
4+
APPLE: 'apple',
5+
} as const;
6+
7+
export type PROVIDER = (typeof PROVIDER)[keyof typeof PROVIDER];

0 commit comments

Comments
 (0)