二、2.99元搭建一个摸鱼网站---nest后端项目初始化

77 阅读2分钟

前言

体验地址 weblog.lihk.top

是的,你没有看错,就是2.99元,前期需要准备一个serv00账号,cloundfare账号,阿里云域名(没有的话也可以不准备)

所使用的代码工具:cursor

前端使用vue3+ts+antd+vite,后端是使用nest+mysql

前期项目搭建使用cursor搭建,逐步搭建好

nest全局统一返回

接口统一返回的格式为code、msg、data

// 全局统一返回
import {
  ArgumentsHost,
  Catch,
  ExceptionFilter,
  HttpException,
} from '@nestjs/common';

@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse();
    const status = exception.getStatus();
    const exceptionResponse = exception.getResponse();

    response.status(status).json({
      code: status,
      msg: exceptionResponse['message'] || 'An error occurred',
      data: null,
    });
  }
}

连接mysql数据库


import { TypeOrmModuleOptions } from '@nestjs/typeorm';
import * as dotenv from 'dotenv';

dotenv.config(); // 加载 .env 文件

export const databaseConfig: TypeOrmModuleOptions = {
  type: 'mysql', // 数据库类型,可更改为 'postgres', 'sqlite' 等
  host: process.env.DATABASE_HOST,
  port: parseInt(process.env.DATABASE_PORT, 10),
  username: process.env.DATABASE_USERNAME,
  password: process.env.DATABASE_PASSWORD,
  database: process.env.DATABASE_NAME,
  entities: [__dirname + '/../**/*.entity.{js,ts}'], // 实体文件路径
  synchronize: true, // 是否自动同步数据库,
  logging: true, // 开启日志输出
  // 设置为utf8mb4字符集
  charset: 'utf8mb4',
  extra: {
    charset: 'utf8mb4', // 这里也指定字符集
  },
};

aes接口加密

import {
  CallHandler,
  ExecutionContext,
  Injectable,
  NestInterceptor,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import * as crypto from 'crypto';
import * as dotenv from 'dotenv';

dotenv.config();
@Injectable()
export class ResponseInterceptor implements NestInterceptor {
  private readonly secretKey;

  constructor() {
    this.secretKey = crypto
      .createHash('sha256')
      .update(process.env.CRYPTO_KEY)
      .digest();
  }
  encryptData(data: any): string {
    // 生成随机 IV
    const iv = crypto.randomBytes(16); // AES-256-CBC 的 IV 长度为 16 字节
    const cipher = crypto.createCipheriv('aes-256-cbc', this.secretKey, iv);

    // 加密数据
    let encrypted = cipher.update(JSON.stringify(data), 'utf8', 'base64');
    encrypted += cipher.final('base64');

    // 返回 IV 和加密数据的组合
    return iv.toString('base64') + ':' + encrypted;
  }
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    return next.handle().pipe(
      map((data) => {
        // 如果没有设置 CRYPTO_KEY,则直接返回数据
        const encryptedData = process.env.CRYPTO_KEY
          ? this.encryptData(data)
          : data;
        return { code: 200, msg: 'success', data: encryptedData };
      }),
    );
  }
}

接口会对其的返回数据进行加密,前端进行解密即可获取正确的接口返回信息 image.png

Swagger接口文档配置

const app = await NestFactory.create(AppModule);
  const config = new DocumentBuilder()
    .setTitle('NestJS Template API')
    .setDescription('API documentation for the NestJS template')
    .setVersion('1.0')
    .addBearerAuth(
      {
        type: 'http',
        scheme: 'bearer',
        bearerFormat: 'JWT',
        in: 'header',
        name: 'Authorization',
      },
      'bearer',
    )
    .addSecurityRequirements('bearer')
    .build();
  const document = SwaggerModule.createDocument(app, config);
  
  //  Swagger项目中使用
  @Get()
  @ApiOperation({ summary: '获取文章列表' })
  async getArticles(
    @Query('page') page: number = 1,
    @Query('pageSize') pageSize: number = 10,
    @Query('categoryId') categoryId?: number, // 可选参数
  ) {
    return this.articleService.findAll(page, pageSize, categoryId);
  }

实际运行效果 image.png

身份验证

在有需要的接口前面使用,这个接口就需要前端登录之后传token才可以查询到数据

import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { CanActivate, ExecutionContext } from '@nestjs/common';

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private readonly jwtService: JwtService) {}

  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest();
    const token = request.headers['authorization']?.split(' ')[1]; // 获取 Bearer token

    if (!token) {
      return false; // 没有 token,返回 false
    }

    try {
      const decoded = this.jwtService.verify(token); // 验证 token
      console.log('🚀 ~ AuthGuard ~ canActivate ~ decoded:', decoded);
      request.user = decoded; // 在请求中添加用户信息
      return true; // 验证成功
    } catch (e) {
      console.log('🚀 ~ AuthGuard ~ canActivate ~ e:', e);
      return false; // 验证失败
    }
  }
}
// 项目中使用
import { AuthGuard } from 'src/auth/auth.guard';
@UseGuards(AuthGuard)

image.png