-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
19 changed files
with
626 additions
and
207 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
export const fileTypeSchema = ({ | ||
type: 'object', | ||
additionalProperties: true, | ||
properties: { | ||
size: { type: 'number', nullable: false }, | ||
path: { type: 'string', format: 'uri-template', nullable: false }, | ||
name: { type: 'string', nullable: false }, | ||
type: { type: 'string', pattern: 'application/octet-stream', nullable: false }, | ||
}, | ||
nullable: false, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,28 +1,36 @@ | ||
// import Router from '@koa/router'; | ||
// import koaBody from 'koa-body'; | ||
// import { Context } from 'koa'; | ||
// import Container from 'typedi'; | ||
// import _ from 'lodash'; | ||
// import { BlogErrorCode } from '@src/common/error/BlogErrorCode'; | ||
// import * as URL from '../common/constant/URL'; | ||
// import PostService from './PostService'; | ||
// | ||
// const postRouter = new Router(); | ||
// const koaBodyOptions = { | ||
// multipart: true, | ||
// }; | ||
// const postService: PostService = Container.get(PostService); | ||
// | ||
// postRouter.post(`${URL.PREFIX.API}${URL.ENDPOINT.POST}`, koaBody(koaBodyOptions), (ctx: Context) => { | ||
// if (_.isEmpty(ctx.request.files) || _.has(ctx.request.files!.post)) { | ||
// throw new Error(BlogErrorCode.FILE_NOT_UPLOADED.code); | ||
// } | ||
// | ||
// postService.createPost({ | ||
// post: ctx.request.files!.post, | ||
// ...ctx.request.body, | ||
// }); | ||
// ctx.status = 200; | ||
// }); | ||
// | ||
// export default postRouter; | ||
import _ from 'lodash'; | ||
import Router from '@koa/router'; | ||
import koaBody from 'koa-body'; | ||
import { Context } from 'koa'; | ||
import Container from 'typedi'; | ||
import * as http2 from 'http2'; | ||
import { File } from 'formidable'; | ||
import * as URL from '@src/common/constant/URL'; | ||
import { getValidatedRequestDtoOf } from '@src/common/validation/DtoValidationUtil'; | ||
import PostService from '@src/post/PostService'; | ||
import BlogError from '@src/common/error/BlogError'; | ||
import { CreateNewPostRequestDto, CreateNewPostRequestSchema } from '@src/post/dto/PostRequestDto'; | ||
import { fileTypeSchema } from '@src/common/validation/ObjectTypeSchema'; | ||
import { BlogErrorCode } from '@src/common/error/BlogErrorCode'; | ||
|
||
const postRouter = new Router(); | ||
const koaBodyOptions = { | ||
multipart: true, | ||
}; | ||
const postService: PostService = Container.get(PostService); | ||
|
||
postRouter.post(`${URL.PREFIX.API}${URL.ENDPOINT.POST}${URL.BEHAVIOR.NEW}`, koaBody(koaBodyOptions), (ctx: Context) => { | ||
if (_.isEmpty(ctx.request.files)) { | ||
throw new BlogError(BlogErrorCode.FILE_NOT_UPLOADED); | ||
} | ||
|
||
const requestDto: CreateNewPostRequestDto = getValidatedRequestDtoOf(CreateNewPostRequestSchema, ctx.request.body); | ||
const post: File = getValidatedRequestDtoOf(fileTypeSchema, ctx.request.files!.post) as File; | ||
|
||
postService.createNewPost({ ...requestDto, post }) | ||
.then(() => { | ||
ctx.status = http2.constants.HTTP_STATUS_CREATED; | ||
}); | ||
}); | ||
|
||
export default postRouter; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,32 +1,100 @@ | ||
// import { Service } from 'typedi'; | ||
// import fs from 'fs'; | ||
// import { CreatePostParamDto } from '@src/post/dto/PostParamDto'; | ||
// import PostRepository from '@src/post/repository/PostRepository'; | ||
// | ||
// @Service() | ||
// export default class PostService { | ||
// public constructor(private readonly postRepository: PostRepository) { | ||
// } | ||
// | ||
// public createPost(paramDto: CreatePostParamDto): void { | ||
// const { post } = paramDto; | ||
// const rawContent: string = this.readPostContent(post.path); | ||
// const renderedContent = this.renderContent(rawContent); | ||
// this.postRepository.createPost({ | ||
// ...paramDto, | ||
// title: post.name, | ||
// rawContent, | ||
// renderedContent, | ||
// createdDate: new Date(), | ||
// }); | ||
// } | ||
// | ||
// private readPostContent(path: string): string { | ||
// return fs.readFileSync(path).toString(); | ||
// } | ||
// | ||
// private renderContent(rawContent: string): string { | ||
// // TODO: 렌더링 로직 구현 | ||
// return rawContent; | ||
// } | ||
// } | ||
import fs from 'fs'; | ||
import _ from 'lodash'; | ||
import { Service } from 'typedi'; | ||
import { Types } from 'mongoose'; | ||
import PostMetaRepository from '@src/post/repository/PostMetaRepository'; | ||
import PostRepository from '@src/post/repository/PostRepository'; | ||
import CategoryRepository from '@src/category/CategoryRepository'; | ||
import SeriesRepository from '@src/series/SeriesRepository'; | ||
import TagRepository from '@src/tag/TagRepository'; | ||
import { CreatePostMetaRepoParamDto } from '@src/post/dto/PostMetaRepoParamDto'; | ||
import { CreateNewPostParamDto } from '@src/post/dto/PostParamDto'; | ||
import { CreatePostRepoParamDto } from '@src/post/dto/PostRepoParamDto'; | ||
import { CategoryDoc } from '@src/category/Category'; | ||
import { SeriesDoc } from '@src/series/Series'; | ||
import { TagDoc } from '@src/tag/Tag'; | ||
import BlogError from '@src/common/error/BlogError'; | ||
import { BlogErrorCode } from '@src/common/error/BlogErrorCode'; | ||
|
||
@Service() | ||
export default class PostService { | ||
public constructor( | ||
private readonly postMetaRepository: PostMetaRepository, | ||
private readonly postRepository: PostRepository, | ||
private readonly categoryRepository: CategoryRepository, | ||
private readonly seriesRepository: SeriesRepository, | ||
private readonly tagRepository: TagRepository, | ||
) {} | ||
|
||
public async createNewPost(paramDto: CreateNewPostParamDto): Promise<void> { | ||
const currentDate = new Date(); | ||
const createPostMetaRepoParamDto: CreatePostMetaRepoParamDto = await this.makeCreatePostMetaRepoParamDto(paramDto, currentDate); | ||
const postNo = await this.postMetaRepository.createPostMeta(createPostMetaRepoParamDto); | ||
const createPostRepoParamDto: CreatePostRepoParamDto = this.makeCreatePostRepoParamDto(postNo, paramDto, currentDate); | ||
await this.postRepository.createPost(createPostRepoParamDto); | ||
} | ||
|
||
private async makeCreatePostMetaRepoParamDto(paramDto: CreateNewPostParamDto, currentDate: Date): Promise<CreatePostMetaRepoParamDto> { | ||
const createPostMetaRepoParamDto: CreatePostMetaRepoParamDto = { | ||
createdDate: currentDate, | ||
isPrivate: _.isNil(paramDto.isPrivate) ? false : paramDto.isPrivate, | ||
}; | ||
if (!_.isNil(paramDto.categoryName)) { | ||
const categoryList: CategoryDoc[] = await this.categoryRepository.findCategory({ name: paramDto.categoryName }); | ||
if (_.isEmpty(categoryList)) { | ||
throw new BlogError(BlogErrorCode.CATEGORY_NOT_FOUND, [paramDto.categoryName, 'name']); | ||
} | ||
Object.assign(createPostMetaRepoParamDto, { categoryId: categoryList[0]._id }); | ||
} | ||
if (!_.isNil(paramDto.seriesName)) { | ||
const seriesList: SeriesDoc[] = await this.seriesRepository.findSeries({ name: paramDto.seriesName }); | ||
if (_.isEmpty(seriesList)) { | ||
throw new BlogError(BlogErrorCode.SERIES_NOT_FOUND, [paramDto.seriesName, 'name']); | ||
} | ||
Object.assign(createPostMetaRepoParamDto, { seriesId: seriesList[0]._id }); | ||
} | ||
if (!_.isNil(paramDto.tagNameList)) { | ||
const tagList: TagDoc[] = await this.tagRepository.findTag({ | ||
findTagByNameDto: { | ||
nameList: paramDto.tagNameList, | ||
isOnlyExactNameFound: true, | ||
}, | ||
}); | ||
if (tagList.length !== paramDto.tagNameList.length) { | ||
const failedToFindTagNameList = _.difference(paramDto.tagNameList, tagList.map((tag) => tag.name)); | ||
throw new BlogError(BlogErrorCode.TAG_NOT_FOUND, ['name', failedToFindTagNameList.join(', ')]); | ||
} | ||
Object.assign(createPostMetaRepoParamDto, { tagIdList: tagList.map((tag) => tag._id) }); | ||
} | ||
return createPostMetaRepoParamDto; | ||
} | ||
|
||
private makeCreatePostRepoParamDto(postNo: number, paramDto: CreateNewPostParamDto, currentDate: Date): CreatePostRepoParamDto { | ||
const { post } = paramDto; | ||
const rawContent: string = this.readPostContent(post.path); | ||
const renderedContent = this.renderContent(rawContent); | ||
const createPostRepoParamDto: CreatePostRepoParamDto = { | ||
postNo, | ||
title: post.name as string, | ||
rawContent, | ||
renderedContent, | ||
language: paramDto.language, | ||
thumbnailContent: paramDto.thumbnailContent, | ||
lastUpdatedDate: currentDate, | ||
isLatestVersion: true, | ||
}; | ||
if (!_.isNil(paramDto.thumbnailImageId)) { | ||
Object.assign(createPostRepoParamDto, { thumbnailImageId: Types.ObjectId(paramDto.thumbnailImageId) }); | ||
} | ||
return createPostRepoParamDto; | ||
} | ||
|
||
private readPostContent(path: string): string { | ||
return fs.readFileSync(path).toString(); | ||
} | ||
|
||
private renderContent(rawContent: string): string { | ||
// TODO: 렌더링 로직 구현 | ||
return rawContent; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,9 @@ | ||
import { Types } from 'mongoose'; | ||
|
||
export interface CreatePostMetaRepoParamDto { | ||
postNo: number; | ||
categoryId?: Types.ObjectId; | ||
tagIdList?: Types.ObjectId[]; | ||
seriesId?: Types.ObjectId; | ||
tagIdList?: Types.ObjectId[]; | ||
createdDate: Date; | ||
isPrivate?: boolean; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { JSONSchemaType } from 'ajv'; | ||
import Language from '@src/common/constant/Language'; | ||
|
||
export interface CreateNewPostRequestDto { | ||
// post meta | ||
categoryName?: string; | ||
tagNameList?: string[]; | ||
seriesName?: string; | ||
isPrivate?: boolean; | ||
|
||
// post | ||
language: Language; | ||
thumbnailContent: string; | ||
thumbnailImageId?: string; | ||
} | ||
|
||
export const CreateNewPostRequestSchema: JSONSchemaType<CreateNewPostRequestDto> = { | ||
type: 'object', | ||
additionalProperties: false, | ||
properties: { | ||
categoryName: { type: 'string', nullable: true }, | ||
tagNameList: { type: 'array', nullable: true, items: { type: 'string' } }, | ||
seriesName: { type: 'string', nullable: true }, | ||
isPrivate: { type: 'boolean', nullable: true }, | ||
|
||
language: { type: 'string', nullable: false }, | ||
thumbnailContent: { type: 'string', nullable: false }, | ||
thumbnailImageId: { type: 'string', nullable: true }, | ||
}, | ||
required: ['language', 'thumbnailContent'], | ||
}; |
Oops, something went wrong.