From e11e1a7f598993c0173dd585962b02335177210b Mon Sep 17 00:00:00 2001 From: luoer <952222@163.com> Date: Tue, 26 Sep 2023 10:58:46 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0Docker=E9=83=A8?= =?UTF-8?q?=E7=BD=B2=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .dockerignore | 7 + .gitea/workflows/deploy.yaml | 55 + Dockerfile | 20 + README.md | 16 +- content/database/db.sqlite | Bin 163840 -> 163840 bytes graph.json => content/development/graph.json | 0 content/development/openapi.json | 2066 +++++++++++++++++ package.json | 22 +- src/app.module.ts | 6 +- src/common/response/notcaptured.filter.ts | 4 +- src/content/category/category.controller.ts | 59 + src/content/category/category.module.ts | 13 + src/content/category/category.service.ts | 55 + .../category/dto/create-category.dto.ts | 51 + src/content/category/dto/find-category.dto.ts | 13 + .../category/dto/update-category.dto.ts | 4 + .../category/entities/category.entity.ts | 48 + src/content/category/index.ts | 4 + src/content/content.module.ts | 12 + .../post/dto/create-post.dto.ts | 0 .../post/dto/find-post.dto.ts | 0 .../post/dto/update-post.dto.ts | 0 .../post/entities/post.entity.ts | 0 src/{modules => content}/post/index.ts | 0 .../post/post.controller.ts | 0 src/{modules => content}/post/post.module.ts | 0 src/{modules => content}/post/post.service.ts | 0 src/modules/user/entities/user.entity.ts | 2 +- .../upload/dto/create-upload.dto.ts | 0 .../upload/dto/update-upload.dto.ts | 0 .../upload/entities/upload.entity.ts | 0 src/{modules => storage}/upload/index.ts | 0 .../upload/upload.controller.ts | 0 .../upload/upload.module.ts | 0 .../upload/upload.service.ts | 0 35 files changed, 2440 insertions(+), 17 deletions(-) create mode 100644 .dockerignore create mode 100644 .gitea/workflows/deploy.yaml create mode 100644 Dockerfile rename graph.json => content/development/graph.json (100%) create mode 100644 content/development/openapi.json create mode 100644 src/content/category/category.controller.ts create mode 100644 src/content/category/category.module.ts create mode 100644 src/content/category/category.service.ts create mode 100644 src/content/category/dto/create-category.dto.ts create mode 100644 src/content/category/dto/find-category.dto.ts create mode 100644 src/content/category/dto/update-category.dto.ts create mode 100644 src/content/category/entities/category.entity.ts create mode 100644 src/content/category/index.ts create mode 100644 src/content/content.module.ts rename src/{modules => content}/post/dto/create-post.dto.ts (100%) rename src/{modules => content}/post/dto/find-post.dto.ts (100%) rename src/{modules => content}/post/dto/update-post.dto.ts (100%) rename src/{modules => content}/post/entities/post.entity.ts (100%) rename src/{modules => content}/post/index.ts (100%) rename src/{modules => content}/post/post.controller.ts (100%) rename src/{modules => content}/post/post.module.ts (100%) rename src/{modules => content}/post/post.service.ts (100%) rename src/{modules => storage}/upload/dto/create-upload.dto.ts (100%) rename src/{modules => storage}/upload/dto/update-upload.dto.ts (100%) rename src/{modules => storage}/upload/entities/upload.entity.ts (100%) rename src/{modules => storage}/upload/index.ts (100%) rename src/{modules => storage}/upload/upload.controller.ts (100%) rename src/{modules => storage}/upload/upload.module.ts (100%) rename src/{modules => storage}/upload/upload.service.ts (100%) diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..2963048 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +dist +node_modules +scripts +.git +.gitea +.gitignore +README.md \ No newline at end of file diff --git a/.gitea/workflows/deploy.yaml b/.gitea/workflows/deploy.yaml new file mode 100644 index 0000000..fbc3be4 --- /dev/null +++ b/.gitea/workflows/deploy.yaml @@ -0,0 +1,55 @@ +name: 自动部署 + +on: + push: + branches: + - master + paths-ignore: + - .gitignore + - .vscode/** + - README.md + +env: + docker_host: ${{ secrets.DOCKER_REGISTRY }} + docker_name: ${{ secrets.DOCKER_REGISTRY }}/${{ gitea.repository }} + docker_user: ${{ secrets.DOCKER_USERNAME }} + docker_pass: ${{ secrets.DOCKER_PASSWORD }} + deploy_host: ${{ secrets.DEPLOY_HOSTNAME }} + deploy_port: ${{ secrets.DEPLOY_PORT }} + deploy_user: ${{ secrets.DEPLOY_USERNAME }} + deploy_pass: ${{ secrets.DEPLOY_PASSWORD }} + deploy_name: demo_server + +jobs: + build: + runs-on: ubuntu-latest + container: + image: catthehacker/ubuntu:act-latest + steps: + - name: 检出代码 + id: checkout + uses: https://gitea.com/actions/checkout@v3 + + - name: 构建镜像 + run: | + docker build -t ${{ env.docker_name }}:latest . + + - name: 登陆镜像 + run: | + docker login -u "${{env.docker_user}}" -p "${{env.docker_pass}}" ${{env.docker_host}} + + - name: 推送镜像 + shell: bash + run: | + docker push ${{ env.docker_name }}:latest + + - name: 更新服务 + uses: http://git.dev.juetan.cn/mirror/ssh-action@v1.0.0 + with: + host: ${{ env.deploy_host }} + port: ${{ env.deploy_port }} + username: ${{ env.deploy_user }} + password: ${{ env.deploy_pass }} + script: | + docker service ls | grep -q ${{ env.deploy_name }} || exit 0 + docker service update --image ${{ env.docker_name }}:latest ${{ env.deploy_name }} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..252567f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +FROM node:18-alpine As dev +RUN apk update && apk add sqlite +RUN apk add --no-cache --virtual .build-deps g++ gcc libgcc libstdc++ linux-headers make python3 +WORKDIR /app +COPY package*.json . +RUN npm install +COPY . . +RUN npm run build && npm prune --production + +FROM node:18-alpine As build +WORKDIR /app +ENV NODE_ENV production +COPY --from=dev /app/content/ ./content/ +COPY --from=dev /app/dist/ ./dist/ +COPY --from=dev /app/node_modules/ ./node_modules/ +COPY --from=dev /app/.env ./ +COPY --from=dev /app/package.json ./ + +EXPOSE 3030 +CMD [ "node", "./dist/main.js" ] diff --git a/README.md b/README.md index 57b4219..182c257 100644 --- a/README.md +++ b/README.md @@ -26,4 +26,18 @@ - 角色模块 - 权限模块 - 上传模块 -- 文章模块 \ No newline at end of file +- 文章模块 + +## 部署 + +目前基于 Gitea 和 Gitea Actions 实现,大致流程是这样的:提交代码到 Gitea 仓库后,触发流水线任务进行构建并打包成 Docker 镜像,推送到 Gitea 自带的软件包仓库,然后登陆生产服务器执行更新命令。 + +使用 Github Actions 也是可以的,两者使用上是兼容的。本仓库有关部署的内容涉及三个地方,不需要的话可自行删除,如下: + +- Dockerfile 构建镜像的配置文件 +- .dockerignore 配置哪些文件应该被忽略掉 +- .gitea/workflows/depoy.yaml 流水线任务的配置文件,语法上与 Github Actions 一致 + +## 最后 + +如果你在使用过程中遇到问题,欢迎在 Issue 中提问。 \ No newline at end of file diff --git a/content/database/db.sqlite b/content/database/db.sqlite index 55c66e940197e08fb39516f6ad8e889231a50959..ec533ca6848137c18ba0c17aa1da57502739f67a 100644 GIT binary patch delta 2168 zcmcgtUrbt87{3Q}{;7H~CUe$WDA`(Ubt1TCa}U$4u32Kb+G(3*50)yk!~zD<%w!1& z0*Zf7ph*b@`RX{;QyhkccJHTJamTlFI_Iq*2_^*dSq|3vhQw zBx$J5vWeycY`_dhEh=@tRx{9zf?F_XOD@d9xF4GQ&>e)f5FK5HW_Qv)%&e|L^CaVm z|Drd*Q5}UQ*Xfvzi8|?>Q8+z<*1w|TJ~rZqW)q^k(7Fh1erWNciD9(1oQg5|kz3DN zHjy{PShv~0FdJKiRv-It1TKw0PXtay(HxcBbE4o3K2PtBGj0#Af>wP9FT9o%*r{S{Rwt&2kz=|QWS8|;m>d& z9dXg&Ww?7EAHJ*A_BGbkbq^?&Lkgv=NB(M;T3J^w7Jq{6W~LPzO?kIVb1>JX*m&4T zY&n}Rc@Oy93U04GZ@-?y?Zus`=e|D7tsn_0{t!gPEBp&mX~7ZhrMNL!T=v9wqVof) zZu!RwmAqSUxH?=sb8Y_qby-n9r}!dK;wZj|%bqRP8G1MwL0l>|DzPB)pD!YLt+8JE zPl8Bq($OfZpFoZ{a%{8fdvYzaPO-5EY=C;hM{^cTJ>v>Pn-$Kiq~hnCDfvWF-GZZj7^mQr z2PZ`%9%ve4gGMx_Px_)50}LAwWlJ?R?1rHYxJNO*b&M0bJ#capP8!kTFf^@UL7ExY zI_sN&kq30lO-CF!2J4M6t|dR8he30y@X9zHS;GG<>0C-$CX-GRoH8W+^U&(TN#O*A zL;h6`*E4d+GJLw{$v)ZX%QAer|8x4X4Btm5Iem$o1eXVzsJwk}HupWyL|nI*@WG!y6(y}_;DXi#SV zvgnj9ll($zr%$GltF)bp?sN`(y|~Nk>w)EiG|FRdilO74Bgl^*5DG-Xr|HuWDH{#W zwBt%=(v^DubET5!IXYhHB-lE@ed2#Oxz5{K*+$LD(^fwG(LH+5k^;ONwzo!cBcId96l_eeDyr**-E(%1GzvjUFrejn#l?R`bunZ z%n9Q1@{Db@C5cHnsc_%I8Eo76UNUwuYOuvIZV@*%Wo%;wvQ~jut%YDM;Gk<3)?pV{ zR0N7mzQ``Qz4AF@G2?cJgN&R^;+zK<#KpC>8QX-x%Gq>4@x#Ed{l+WC>x|ny4>Oi9 uvM?|(GEe_{mhqun3%fSExUn%~yJ>SRl% { + return this.categoryService.findOne(id); + } + + /** + * 根据ID更新分类 + */ + @Patch(':id') + updateCategory(@Param('id', ParseIntPipe) id: number, @Body() updateCategoryDto: UpdateCategoryDto) { + return this.categoryService.update(+id, updateCategoryDto); + } + + /** + * 根据ID删除分类 + */ + @Delete(':id') + delCategory(@Param('id', ParseIntPipe) id: number) { + return this.categoryService.remove(+id); + } +} diff --git a/src/content/category/category.module.ts b/src/content/category/category.module.ts new file mode 100644 index 0000000..5d60e22 --- /dev/null +++ b/src/content/category/category.module.ts @@ -0,0 +1,13 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { Category } from './entities/category.entity'; +import { CategoryController } from './category.controller'; +import { CategoryService } from './category.service'; + +@Module({ + imports: [TypeOrmModule.forFeature([Category])], + controllers: [CategoryController], + providers: [CategoryService], + exports: [CategoryService], +}) +export class CategoryModule {} diff --git a/src/content/category/category.service.ts b/src/content/category/category.service.ts new file mode 100644 index 0000000..b38ecdb --- /dev/null +++ b/src/content/category/category.service.ts @@ -0,0 +1,55 @@ +import { BaseService } from '@/common/base'; +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Like, Repository } from 'typeorm'; +import { CreateCategoryDto } from './dto/create-category.dto'; +import { FindCategoryDto } from './dto/find-category.dto'; +import { UpdateCategoryDto } from './dto/update-category.dto'; +import { Category } from './entities/category.entity'; + +@Injectable() +export class CategoryService extends BaseService { + constructor(@InjectRepository(Category) private categoryRepository: Repository) { + super(); + } + + /** + * 新增分类 + */ + async create(createCategoryDto: CreateCategoryDto) { + const category = this.categoryRepository.create(createCategoryDto); + await this.categoryRepository.save(category); + return category.id; + } + + /** + * 条件/分页查询 + */ + async findMany(findCategorydto: FindCategoryDto) { + const { page, size } = findCategorydto; + const { skip, take } = this.formatPagination(page, size, true); + return this.categoryRepository.findAndCount({ skip, take }); + } + + /** + * 根据ID查询 + */ + findOne(idOrOptions: number | Partial) { + const where = typeof idOrOptions === 'number' ? { id: idOrOptions } : (idOrOptions as any); + return this.categoryRepository.findOne({ where }); + } + + /** + * 根据ID更新 + */ + update(id: number, updateCategoryDto: UpdateCategoryDto) { + return this.categoryRepository.update(id, updateCategoryDto); + } + + /** + * 根据ID删除(软删除) + */ + remove(id: number) { + return this.categoryRepository.softDelete(id); + } +} diff --git a/src/content/category/dto/create-category.dto.ts b/src/content/category/dto/create-category.dto.ts new file mode 100644 index 0000000..37dc045 --- /dev/null +++ b/src/content/category/dto/create-category.dto.ts @@ -0,0 +1,51 @@ +import { IsEnum, IsNumber, IsOptional, IsString } from 'class-validator'; + +export class CreateCategoryDto { + /** + * 分类名称 + * @example '待分类' + */ + @IsString() + title: string; + /** + * 分类别名 + * @example 'default' + */ + @IsString() + slug: string; + /** + * 分类描述 + * @example '默认分类' + */ + @IsString() + @IsOptional() + description?: string; + /** + * 分类图标 + * @example 'default' + */ + @IsString() + @IsOptional() + icon?: string; + /** + * 分类排序 + * @example 0 + */ + @IsNumber() + @IsOptional() + sort?: number; + /** + * 分类类型 + * @example 'category' + */ + @IsEnum(['category', 'tag']) + @IsOptional() + type: 'category' | 'tag'; + /** + * 父级分类ID + * @example 0 + */ + @IsNumber() + @IsOptional() + parentId?: number; +} diff --git a/src/content/category/dto/find-category.dto.ts b/src/content/category/dto/find-category.dto.ts new file mode 100644 index 0000000..c46d63e --- /dev/null +++ b/src/content/category/dto/find-category.dto.ts @@ -0,0 +1,13 @@ +import { PaginationDto } from '@/common/response'; +import { IntersectionType } from '@nestjs/swagger'; +import { IsOptional, IsString } from 'class-validator'; + +export class FindCategoryDto extends IntersectionType(PaginationDto) { + /** + * 字段描述(Swagger用途) + * @example '示例值' + */ + @IsOptional() + @IsString() + demo?: string; +} diff --git a/src/content/category/dto/update-category.dto.ts b/src/content/category/dto/update-category.dto.ts new file mode 100644 index 0000000..d713b9b --- /dev/null +++ b/src/content/category/dto/update-category.dto.ts @@ -0,0 +1,4 @@ +import { PartialType } from '@nestjs/swagger'; +import { CreateCategoryDto } from './create-category.dto'; + +export class UpdateCategoryDto extends PartialType(CreateCategoryDto) {} diff --git a/src/content/category/entities/category.entity.ts b/src/content/category/entities/category.entity.ts new file mode 100644 index 0000000..fa71eb2 --- /dev/null +++ b/src/content/category/entities/category.entity.ts @@ -0,0 +1,48 @@ +import { BaseEntity } from '@/database'; +import { Column, Entity, ManyToOne, OneToMany } from 'typeorm'; + +@Entity({ orderBy: { id: 'DESC' } }) +export class Category extends BaseEntity { + /** + * 分类名称 + * @example '待分类' + */ + @Column() + title: string; + /** + * 分类别名 + * @example 'default' + */ + @Column() + slug: string; + /** + * 分类描述 + * @example '默认分类' + */ + @Column({ nullable: true }) + description?: string; + /** + * 分类图标 + * @example 'default' + */ + @Column({ nullable: true }) + icon?: string; + /** + * 分类排序 + * @example 0 + */ + @Column({ default: 0 }) + sort?: number; + /** + * 分类类型 + * @example 'category' + */ + @Column({ default: 'category' }) + type?: 'category' | 'tag'; + /** + * 父级分类ID + * @example 0 + */ + @Column({ default: 0, nullable: true }) + parentId?: number; +} diff --git a/src/content/category/index.ts b/src/content/category/index.ts new file mode 100644 index 0000000..bdabbf6 --- /dev/null +++ b/src/content/category/index.ts @@ -0,0 +1,4 @@ +export * from './entities/category.entity'; +export * from './category.controller'; +export * from './category.module'; +export * from './category.service'; diff --git a/src/content/content.module.ts b/src/content/content.module.ts new file mode 100644 index 0000000..1f2e5fc --- /dev/null +++ b/src/content/content.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { CategoryModule } from './category'; + +@Module({ + imports: [ + /** + * 分类模块 + */ + CategoryModule + ], +}) +export class ContentModule {} diff --git a/src/modules/post/dto/create-post.dto.ts b/src/content/post/dto/create-post.dto.ts similarity index 100% rename from src/modules/post/dto/create-post.dto.ts rename to src/content/post/dto/create-post.dto.ts diff --git a/src/modules/post/dto/find-post.dto.ts b/src/content/post/dto/find-post.dto.ts similarity index 100% rename from src/modules/post/dto/find-post.dto.ts rename to src/content/post/dto/find-post.dto.ts diff --git a/src/modules/post/dto/update-post.dto.ts b/src/content/post/dto/update-post.dto.ts similarity index 100% rename from src/modules/post/dto/update-post.dto.ts rename to src/content/post/dto/update-post.dto.ts diff --git a/src/modules/post/entities/post.entity.ts b/src/content/post/entities/post.entity.ts similarity index 100% rename from src/modules/post/entities/post.entity.ts rename to src/content/post/entities/post.entity.ts diff --git a/src/modules/post/index.ts b/src/content/post/index.ts similarity index 100% rename from src/modules/post/index.ts rename to src/content/post/index.ts diff --git a/src/modules/post/post.controller.ts b/src/content/post/post.controller.ts similarity index 100% rename from src/modules/post/post.controller.ts rename to src/content/post/post.controller.ts diff --git a/src/modules/post/post.module.ts b/src/content/post/post.module.ts similarity index 100% rename from src/modules/post/post.module.ts rename to src/content/post/post.module.ts diff --git a/src/modules/post/post.service.ts b/src/content/post/post.service.ts similarity index 100% rename from src/modules/post/post.service.ts rename to src/content/post/post.service.ts diff --git a/src/modules/user/entities/user.entity.ts b/src/modules/user/entities/user.entity.ts index 1268278..9e4c116 100644 --- a/src/modules/user/entities/user.entity.ts +++ b/src/modules/user/entities/user.entity.ts @@ -1,5 +1,5 @@ import { BaseEntity } from '@/database'; -import { Post } from '@/modules/post'; +import { Post } from '@/content/post'; import { Role } from '@/modules/role'; import { ApiHideProperty } from '@nestjs/swagger'; import { Exclude } from 'class-transformer'; diff --git a/src/modules/upload/dto/create-upload.dto.ts b/src/storage/upload/dto/create-upload.dto.ts similarity index 100% rename from src/modules/upload/dto/create-upload.dto.ts rename to src/storage/upload/dto/create-upload.dto.ts diff --git a/src/modules/upload/dto/update-upload.dto.ts b/src/storage/upload/dto/update-upload.dto.ts similarity index 100% rename from src/modules/upload/dto/update-upload.dto.ts rename to src/storage/upload/dto/update-upload.dto.ts diff --git a/src/modules/upload/entities/upload.entity.ts b/src/storage/upload/entities/upload.entity.ts similarity index 100% rename from src/modules/upload/entities/upload.entity.ts rename to src/storage/upload/entities/upload.entity.ts diff --git a/src/modules/upload/index.ts b/src/storage/upload/index.ts similarity index 100% rename from src/modules/upload/index.ts rename to src/storage/upload/index.ts diff --git a/src/modules/upload/upload.controller.ts b/src/storage/upload/upload.controller.ts similarity index 100% rename from src/modules/upload/upload.controller.ts rename to src/storage/upload/upload.controller.ts diff --git a/src/modules/upload/upload.module.ts b/src/storage/upload/upload.module.ts similarity index 100% rename from src/modules/upload/upload.module.ts rename to src/storage/upload/upload.module.ts diff --git a/src/modules/upload/upload.service.ts b/src/storage/upload/upload.service.ts similarity index 100% rename from src/modules/upload/upload.service.ts rename to src/storage/upload/upload.service.ts From 1d2fe70f426c5676159f0049ff7ee74441ea0f0e Mon Sep 17 00:00:00 2001 From: luoer <952222@163.com> Date: Tue, 26 Sep 2023 17:06:15 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=8F=92=E5=85=A5?= =?UTF-8?q?=E5=88=A0=E9=99=A4=E7=94=A8=E6=88=B7=E5=A4=B1=E8=B4=A5=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- content/database/db.sqlite | Bin 163840 -> 163840 bytes src/app.module.ts | 10 +++++++--- src/database/suscribers/entify.subscriber.ts | 2 +- .../cache/cache.controller.ts | 0 src/{common => storage}/cache/cache.module.ts | 0 .../cache/cache.service.ts | 0 src/{common => storage}/cache/index.ts | 0 7 files changed, 8 insertions(+), 4 deletions(-) rename src/{common => storage}/cache/cache.controller.ts (100%) rename src/{common => storage}/cache/cache.module.ts (100%) rename src/{common => storage}/cache/cache.service.ts (100%) rename src/{common => storage}/cache/index.ts (100%) diff --git a/content/database/db.sqlite b/content/database/db.sqlite index ec533ca6848137c18ba0c17aa1da57502739f67a..bfca6b3fab0d1c3cae11fc62a00b218db8e45d5e 100644 GIT binary patch delta 630 zcmZo@;A&{#njpk;-U;3vW#dd4XsQ~K`!tD z>H@K!ZS8)se(KYXw&xpn8TA7%E zj8iqU0<$5;fy{xhp7r;?*uMd0+-9x2S&S_Fiy53I&#<#o$zz2WZp@%A$$-VJ=2oT# zAj6}~Kp|&piE1}k2-*2YVCNgbEMtjggSj2M@fKDFmLTKZfk6RcBb#LmGRxQ)W)?F8 f14Aq*5`e*q*Qv@tvp~*9HVfTdO%IkmU|awIQkK6u delta 92 zcmZo@;A&{#njp): void | Promise { - event.entity.deletedBy = this.getUser(); + event.entity && (event.entity.deletedBy = this.getUser()); } getUser() { diff --git a/src/common/cache/cache.controller.ts b/src/storage/cache/cache.controller.ts similarity index 100% rename from src/common/cache/cache.controller.ts rename to src/storage/cache/cache.controller.ts diff --git a/src/common/cache/cache.module.ts b/src/storage/cache/cache.module.ts similarity index 100% rename from src/common/cache/cache.module.ts rename to src/storage/cache/cache.module.ts diff --git a/src/common/cache/cache.service.ts b/src/storage/cache/cache.service.ts similarity index 100% rename from src/common/cache/cache.service.ts rename to src/storage/cache/cache.service.ts diff --git a/src/common/cache/index.ts b/src/storage/cache/index.ts similarity index 100% rename from src/common/cache/index.ts rename to src/storage/cache/index.ts