Total Pageviews

Friday 25 October 2024

一个基于nodejs的论坛程序:pincman-forum


构建一个简单的频道<->消息应用,支持TDD,E2E测试,swagger(open api)文档,CI/CD

技术栈

本应用为了简单起见,使用以下技术栈

  • nestjs - 一个使用typescript构建的企业级node.js框架
  • typeorm - 一个node+ts构建的orm
  • class-validator - 用于验证ts类
  • class-transform - 用于序列化ts对象
  • sqlite - 一个简单的嵌入式数据库,如果有需要你可以换成mysql或者postgresql等任意数据库

开发

安装启动

下载源码

git clone https://github.com/pincman/forum

安装pnpm

npm install -g pnpm

安装应用

cd forum && pnpm i

启动应用并热更新

pnpm start:dev

IDE配置

推荐使用Vscode配置开发环境

本应用的代码规范遵循airbnb标准,如果使用其它规范,譬如standard等,请自行调整

为了能身心愉悦的编辑代码,请使用以下命令安装eslint与prettier插件

code --install-extension dbaeumer.vscode-eslint \
  && code esbenp.prettier-vscode

Debug

根据自己需要的调试方式,随意调整./vscode/launch.json文件,在任意文件打上断点,按F5一键Debug

测试接口

在Postman中导入集合(./postman/collection.json)和(./postman/environment.json)环境配置配置,并对接口进行测试,如下图QQ20220712-103327@2x

又或者你可以通过访问https://forum.pincman.com/api-docs来访问swagger(open api)页面进行测试

open api

部署

我目前使用的是gitea+drone的devops方式进行部署,步骤如下

  • 先使用nginx创建一个虚拟主机反向映射到应用的端口
  • 接着把代码push到应用的gitea的仓库
  • gitea通过钩子触发drone(drone.pincman.com)自动构建
  • 构建进度可进入drone控制面板,使用 账户: employer,密码: 123456查看
  • drone构建完毕自动把产出物(即dist包)通过ssh输送到生产服务器
  • drone在输送完编译包后,pm2将自启对应用进行部署

drone配置请参考源代码

QQ20220712-182104@2x

在drone中需要设置以下字段

  • deploy_path: 生产服务器需要部署的路径
  • host: 生产服务器的IP地址
  • ssh_key: 构建服务器连接生成服务器用到的密匙(需要提前将公钥添加到生产服务器的authorized_key中)
  • username: 连接生产服务器的用户名

更简洁的方式

可以自行配置一个Dockerfile或者docker-compose.yml代替pm2+nginx,在drone构建后进行部署

注意事项(备注)

  • 在应用比较大时中使用migration(迁移命令)来代替当前代码中的同步配置,这样在数据库结构更新后不会丢数据,迁移命令和编写方法十分简单,可参考typeorm cli文档,也可以自动利用entity生成迁移
  • 尽可能使用pm2的cluster方式进行部署,这样在一个子进程挂掉的时候不会导致整个应用的崩溃

接口

也可以使用open api(swagger)进行测试,地址: https://forum.pincman.com/api-docs,如下图

open api

频道列表

列出所有频道

API

  • url: /channels
  • method: GET

Response Data: 频道列表数组,每个频道的字段解释请查看频道详情

QQ20220712-112035@2x

频道详情

显示一个频道的详情

API

  • url: /channels/:channel_id
  • method: GET

Response Data

  • id: 当前频道的ID
  • name: 当前频道的名称
  • content: 当前频道的排序
  • createdAt: 当前频道下的消息数量

QQ20220712-112609@2x

创建频道

频道名称具有唯一性,不可重复

API

  • url: /channels
  • method: POST

Body

  • name: 频道名称

Response Data: 请参考频道详情

QQ20220712-112924@2x

频道名称重复将抛出400异常

QQ20220712-113134@2x

更新频道

频道名称具有唯一性,如果已存在则抛出400异常

API

  • url: /channels
  • method: PATCH

Body

  • id: 频道ID
  • name: 频道名称

Response Data: 请参考频道详情

QQ20220712-113430@2x

频道名称重复异常

QQ20220712-113459@2x

删除频道

删除频道的同时会删除其下的所有消息

API

  • url: /channels/:channel_id
  • method: DELETE

Response Data: 请参考频道详情

QQ20220712-113829@2x

消息列表

按创建时间排序,支持分页,支持通过频道过滤,消息内容自动通过防xss处理,标题自动去除html标签

API

  • url: /messages
  • method: GET

Params

  • page: 当前页
  • limit: 每页显示消息数量
  • channel: 只查找指定频道下的消息,通过频道ID指定

Response Data

  • items: 查询出的消息列表,每个消息的字段解释请查看消息详情
  • meta.totalItems: 符合条件的消息的总数量
  • meta.itemCount: 当前页显示数量
  • meta.itemsPerPage: 每一页显示的消息数量
  • meta.totalPages: 总页数
  • meta.currentPage: 当前页

如图

QQ20220712-111508@2x

消息详情

消息内容自动通过防xss处理,标题自动去除html标签

API

  • url: /messages/:message_id
  • method: GET

Response Data

  • id: 当前消息的ID
  • title: 当前消息的标题
  • content: 当前消息的内容
  • createdAt: 当前消息的创建时间

QQ20220712-113926@2x

新增消息

API

  • url: /messages
  • method: POST

Body

  • title: 消息标题
  • content: 消息内容
  • channel: 频道ID

Response Data: 请参考消息详情

新增消息(必须通过channel字段指定分类),对content字段自动过滤部分html标签以防止xss攻击,对title清除全部html标签,如下图

QQ20220712-104634@2x

更新消息

API

  • url:/messages
  • method:PATCH

Body

  • id: 消息ID
  • title: 消息标题
  • content: 消息内容
  • channel: 频道ID

Response Data: 请参考消息详情

更新消息可重设title,content以及关联的channel,如图QQ20220712-105225@2x

删除消息

API

  • url: /messages/:message_id
  • method:DELETE

Response Data: 请参考消息详情

删除消息,并返回相应的数据库对象,如图QQ20220712-105225@2x

编码

文件结构

src
├── app.controller.ts                         #框架默认文件(可废弃)
├── app.module.ts                             #框架默认文件(可废弃)
├── app.service.ts                            #框架默认文件(可废弃)
├── config
│   └── database.config.ts                    #数据库配置文件
├── database
│   └── database.sqlite                       #sqlite数据库文件
├── main.ts                                   #应用的启动引导文件
└── modules
    ├── core                                  #核心包
    │   ├── constants.ts                      #常量文件
    │   ├── constraints
    │   ├── core.module.ts                    #核心模块
    │   ├── decorators                        #装饰器集合
    │   ├── helpers.ts                        #一些帮助函数
    │   ├── providers                         #自定义的全局验证管道,序列化拦截器和异常处理过滤器
    │   └── types.ts                          #全局的类型
    └── forum                                 #社区逻辑业务包
        ├── forum.module.ts                   #社区模块
        ├── controllers                       #控制器
        ├── dtos                              #请求数据验证DTO
        ├── entities                          #实体模型
        ├── repositories                      #操作数据的自定义存储类
        ├── services                          #服务类
        └── subscribers                       #用于执行查询钩子的订阅者类
from https://github.com/pincman/forum 

No comments:

Post a Comment