feat: 解决冲突

master
绝弹 2023-09-27 18:42:38 +08:00
commit 4647b829c3
39 changed files with 2367 additions and 21 deletions

View File

@ -5,9 +5,9 @@ on:
branches:
- master
paths-ignore:
- ".gitignore"
- "README.md"
- ".vscode/**"
- .gitignore
- .vscode/**
- README.md
env:
docker_host: ${{ secrets.DOCKER_REGISTRY }}
@ -32,16 +32,16 @@ jobs:
- name: 构建镜像
run: |
docker build -t ${{ env.docker_name }}:latest .
docker build -t ${{ env.docker_name }}:latest .
- name: 登陆镜像
run: |
docker login -u "${{env.docker_user}}" -p "${{env.docker_pass}}" ${{env.docker_host}}
docker login -u "${{env.docker_user}}" -p "${{env.docker_pass}}" ${{env.docker_host}}
- name: 推送镜像
shell: bash
run: |
docker push ${{ env.docker_name }}:latest
docker push ${{ env.docker_name }}:latest
- name: 更新服务
uses: http://git.dev.juetan.cn/mirror/ssh-action@v1.0.0
@ -52,4 +52,4 @@ jobs:
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 }}
docker service update --image ${{ env.docker_name }}:latest ${{ env.deploy_name }}

View File

@ -1,4 +1,6 @@
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
@ -16,4 +18,3 @@ COPY --from=dev /app/package.json ./
EXPOSE 3030
CMD [ "node", "./dist/main.js" ]

View File

@ -26,4 +26,18 @@
- 角色模块
- 权限模块
- 上传模块
- 文章模块
- 文章模块
## 部署
目前基于 Gitea 和 Gitea Actions 实现,大致流程是这样的:提交代码到 Gitea 仓库后,触发流水线任务进行构建并打包成 Docker 镜像,推送到 Gitea 自带的软件包仓库,然后登陆生产服务器执行更新命令。
使用 Github Actions 也是可以的,两者使用上是兼容的。本仓库有关部署的内容涉及三个地方,不需要的话可自行删除,如下:
- Dockerfile 构建镜像的配置文件
- .dockerignore 配置哪些文件应该被忽略掉
- .gitea/workflows/depoy.yaml 流水线任务的配置文件,语法上与 Github Actions 一致
## 最后
如果你在使用过程中遇到问题,欢迎在 Issue 中提问。

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -33,6 +33,7 @@
"@nestjs/config": "^2.3.4",
"@nestjs/jwt": "^10.1.1",
"@nestjs/platform-express": "^9.4.3",
"@nestjs/cache-manager": "^2.1.0",
"axios": "^1.5.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
@ -45,10 +46,8 @@
"typeorm-naming-strategies": "^4.1.0",
"ua-parser-js": "^1.0.36",
"uuid": "^9.0.1",
"winston": "^3.10.0",
"nodemailer": "^6.9.5",
"mysql2": "^3.6.1",
"@nestjs/cache-manager": "^2.1.0",
"cache-manager": "^5.2.3",
"cache-manager-redis-store": "^3.0.1",
"lodash": "^4.17.21",
@ -57,6 +56,7 @@
"multer": "1.4.5-lts.1",
"redis": "^4.6.8",
"sqlite3": "^5.1.6",
"winston": "^3.10.0",
"winston-daily-rotate-file": "^4.7.1"
},
"devDependencies": {
@ -107,4 +107,4 @@
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
}

View File

@ -1,7 +1,7 @@
import { Module } from '@nestjs/common';
import { PostModule } from '@/modules/post';
import { PostModule } from '@/content/post';
import { RoleModule } from '@/modules/role';
import { UploadModule } from '@/modules/upload';
import { UploadModule } from '@/storage/upload';
import { PermissionModule } from '@/modules/permission';
import { ConfigModule } from '@/config';
import { LoggerModule } from '@/common/logger';
@ -12,8 +12,9 @@ import { AuthModule } from '@/modules/auth';
import { UserModule } from '@/modules/user';
import { ResponseModule } from '@/common/response';
import { SerializationModule } from '@/common/serialization';
import { CacheModule } from './common/cache';
import { ScanModule } from './utils/scan.module';
import { CacheModule } from '@/storage/cache';
import { ScanModule } from '@/utils/scan.module';
import { ContentModule } from '@/content/content.module';
@Module({
imports: [
@ -61,6 +62,8 @@ import { ScanModule } from './utils/scan.module';
* @description
*/
DatabaseModule,
/**
*
*/
@ -77,6 +80,8 @@ import { ScanModule } from './utils/scan.module';
*
*/
PermissionModule,
/**
*
*/
@ -85,6 +90,7 @@ import { ScanModule } from './utils/scan.module';
*
*/
PostModule,
ContentModule
],
})
export class AppModule {}

View File

@ -22,7 +22,7 @@ export class AllExecptionFilter implements ExceptionFilter {
return response.status(exception.status).json(
Response.create({
code: ResponseCode.ERROR,
message: '访问的路径不存在',
message: '路径不存在',
}),
);
}
@ -32,7 +32,7 @@ export class AllExecptionFilter implements ExceptionFilter {
return response.status(HttpStatus.UNAUTHORIZED).json(
Response.create({
code: ResponseCode.TOKEN_EXPIRED,
message: '登陆已过期'
message: '登陆令牌已过期'
})
)
}

View File

@ -0,0 +1,59 @@
import { BaseController } from '@/common/base';
import { Respond, RespondType } from '@/common/response';
import { Body, Controller, Delete, Get, Param, Patch, Post, Query, ParseIntPipe } from '@nestjs/common';
import { ApiOkResponse, ApiTags } from '@nestjs/swagger';
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';
import { CategoryService } from './category.service';
@ApiTags('category')
@Controller('categories')
export class CategoryController extends BaseController {
constructor(private categoryService: CategoryService) {
super();
}
/**
*
*/
@Post()
addCategory(@Body() createCategoryDto: CreateCategoryDto) {
return this.categoryService.create(createCategoryDto);
}
/**
* /
*/
@Get()
@Respond(RespondType.PAGINATION)
@ApiOkResponse({ isArray: true, type: Category })
getCategorys(@Query() query: FindCategoryDto) {
return this.categoryService.findMany(query);
}
/**
* ID
*/
@Get(':id')
getCategory(@Param('id', ParseIntPipe) id: number): Promise<Category> {
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);
}
}

View File

@ -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 {}

View File

@ -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<Category>) {
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<Category>) {
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);
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -0,0 +1,4 @@
import { PartialType } from '@nestjs/swagger';
import { CreateCategoryDto } from './create-category.dto';
export class UpdateCategoryDto extends PartialType(CreateCategoryDto) {}

View File

@ -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;
}

View File

@ -0,0 +1,4 @@
export * from './entities/category.entity';
export * from './category.controller';
export * from './category.module';
export * from './category.service';

View File

@ -0,0 +1,12 @@
import { Module } from '@nestjs/common';
import { CategoryModule } from './category';
@Module({
imports: [
/**
*
*/
CategoryModule
],
})
export class ContentModule {}

View File

@ -27,7 +27,7 @@ export class EntitySubscripber implements EntitySubscriberInterface {
}
beforeSoftRemove(event: SoftRemoveEvent<any>): void | Promise<any> {
event.entity.deletedBy = this.getUser();
event.entity && (event.entity.deletedBy = this.getUser());
}
getUser() {

View File

@ -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';