├── .env ├── .eslintrc.json ├── .gitignore ├── README.md ├── ecosystem.config.js ├── package-lock.json ├── package.json ├── src ├── common │ ├── help.ts │ ├── interface │ │ └── index.ts │ ├── logger │ │ ├── index.ts │ │ └── logFile │ │ │ ├── ~.2024-01-02.log │ │ │ └── ~.2024-01-03.log │ └── static │ │ ├── appA │ │ └── index.html │ │ └── appB │ │ └── index.html ├── config.ts ├── controllers │ ├── activeRecord.ts │ └── dataMapper.ts ├── entity │ ├── activeRecord.ts │ ├── dataMapper.ts │ └── index.ts ├── index.ts ├── middleware │ └── index.ts └── types │ └── koa-parameter.d.ts ├── tsconfig.json └── webpack.config.js /.env: -------------------------------------------------------------------------------- 1 | PORT=3000 -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true 5 | }, 6 | "extends": "standard-with-typescript", 7 | "parserOptions": { 8 | "ecmaVersion": "latest", 9 | "sourceType": "module" 10 | }, 11 | "ignorePatterns": ["node_modules/", "dist/", "./ecosystem.config.js"], 12 | "rules": { 13 | "@typescript-eslint/unbound-method": "off", 14 | "@typescript-eslint/strict-boolean-expressions": "off", 15 | "@typescript-eslint/no-floating-promises": "off", 16 | "@typescript-eslint/no-var-requires": "off" 17 | } 18 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | yarn.lock 4 | package-lock.json -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Koa-TypeScript-Template 2 | 3 | 一个为 Node.js 开发者定制的现代化 Koa 框架与 TypeScript 项目模板,专为构建高效、可维护的 API 服务而设计。此项目利用了 TypeScript 的强类型特性,结合 Koa 为开发者提供了高度可扩展的服务端应用架构。它集成了一系列的最佳实践和工具,帮助你快速启动新项目,并专注于业务逻辑的开发。 4 | 5 | ## 特点 6 | 7 | - **装饰器风格的路由声明**:通过 `routing-controllers`,你可以使用装饰器来声明控制器和路由以及中间价,使路由更清晰和易于管理。 8 | - **支持 ORM**:集成的 `typeorm` 提供了数据库操作的强大能力,同时使得数据层代码更加直观和类型安全。 9 | - **日志管理**:使用 `log4js` 来方便地跟踪和记录应用日志,同时支持日志分割和条件日志写入。 10 | - **强大的静态文件服务**:内置 `koa-static` 和 `koa-mount`,方便地为应用程序提供静态文件访问和管理服务,支持多目录挂载。 11 | - **开发效率优化**:借助 `nodemon` 实现开发过程中的自动热重载,提升开发效率。 12 | - **代码规范与格式化**:预配置 `eslint`,确保代码风格一致性和遵守最佳实践。 13 | - **生产环境托管**:基于 `pm2`,实现生产环境的进程管理和自动负载均衡,确保高可用。 14 | - **开箱即用**:集成了多个开发环境和生产环境常用的脚本命令,可快速部署和管理。 15 | - **可扩展性**:模板结构适应多种使用场景,便于根据具体需求添加新功能和服务。 16 | - **Webpack集成**:集成 `Webpack`,配置 `alisa`,提供快速的 `TS` 打包编译,优化项目构建流程(`ts-node` 与 `tsc`打包配置可以上main分支查看)。 17 | 18 | ## 项目结构 19 | ``` 20 | . 21 | ├── src 22 | │ ├── common // 通用工具存放了静态文件和工具函数以及日志文件等 23 | │ ├── controller // controller层,写业务相关逻辑 24 | │ ├── entity // 数据库相关 datasource 和实体 25 | │ ├── logger // 日志入口文件和日志文件存放 26 | │ ├── middleware // middleware中间价,其中定义了鉴权和打日志的中间价 27 | │ ├── types // 自定义 types 文件 28 | │ └── config.ts // 项目入口 index.js 29 | │ └── index.ts // 项目入口 index.js 30 | ├── ecosystem.config.js // pm2 配置 31 | ├── env // 环境参数配置 32 | ├── .eslintrc.json // eslint 配置 33 | ├── .gitignore // git 文件过滤 34 | ├── nodemon.json // nodemon 配置 35 | ├── package.json // package 配置 36 | └── tsconfig.json // ts 配置 37 | ``` 38 | 39 | ## 开始 40 | 41 | 为了运行和使用这个项目模板,你需要先确保安装了 [Node.js](https://nodejs.org/) (推荐使用最新的 LTS 版本)。 42 | 43 | ### 安装依赖 44 | 45 | 在项目的根目录下运行以下命令来安装所有必要的依赖: 46 | 47 | ```bash 48 | npm install 49 | ``` 50 | 51 | ### 启动开发服务器 52 | 53 | ```bash 54 | npm run dev 55 | ``` 56 | 该命令会启动 nodemon,它将监听源文件的改变并自动重启服务器。 57 | 58 | ### 构建项目 59 | 在准备将应用程序部署到生产环境之前,你可以使用以下命令来构建项目: 60 | 61 | ```bash 62 | npm run build 63 | ``` 64 | 这个命令会编译 TypeScript 代码,然后将编译后的 JavaScript文件以及静态资源拷贝到 dist 目录。 65 | 66 | ### 部署项目 67 | 要在生产环境中部署应用程序,请运行: 68 | ```bash 69 | npm run deploy 70 | ``` 71 | 这个命令会使用 pm2 启动 ecosystem.config.js 中定义的应用程序实例,并设置环境变量为生产环境。 72 | 73 | ### 重启应用 74 | 若需重启生产环境中的应用程序,使用以下命令: 75 | ```bash 76 | npm run restart 77 | ``` 78 | 这个命令会让 pm2 重新加载 ecosystem.config.js 中的配置。 79 | 80 | ### 停止应用 81 | 若需停止应用程序,可以使用: 82 | ```bash 83 | npm run stop 84 | ``` 85 | 该命令会停止 pm2 进程管理器中的所有应用程序实例。 86 | 87 | 88 | ## 友情链接 89 | 90 | - Koa2 [Koa (koajs) -- 基于 Node.js 平台的下一代 web 开发框架 \| Koajs 中文文档](https://koa.bootcss.com/) 91 | - Typescript [TypeScript 中文网 · TypeScript——JavaScript 的超集](https://www.tslang.cn/) 92 | - routing-controllers [装饰器风格的路由声明](https://github.com/typestack/routing-controllers) 93 | 94 | ## 贡献 95 | 欢迎任何形式的贡献,如果您有建议或要报告 bug,请通过 issue 进行。 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /ecosystem.config.js: -------------------------------------------------------------------------------- 1 | const os = require('os') 2 | const path = require('path') 3 | const { name } = require('./package.json') 4 | 5 | module.exports = { 6 | apps: [ 7 | { 8 | name, 9 | script: path.resolve(__dirname, './dist/index.js'), 10 | instances: os.cpus().length, 11 | autorestart: true, 12 | watch: false, 13 | env_production: { 14 | NODE_ENV: 'production', 15 | PORT: 8080 16 | } 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "description": "base koa server", 5 | "tags": [ 6 | "orm", 7 | "typescript", 8 | "koa" 9 | ], 10 | "scripts": { 11 | "dev": "cross-env NODE_ENV=development webpack --watch", 12 | "build": "cross-env NODE_ENV=production webpack", 13 | "deploy": "npx pm2 start ecosystem.config.js --env production", 14 | "restart": "pm2 restart ecosystem.config.js --env production", 15 | "stop": "npx pm2 stop ecosystem.config.js", 16 | "lint": "eslint './src/**/*.ts'" 17 | }, 18 | "dependencies": { 19 | "@types/koa-mount": "^4.0.5", 20 | "@types/webpack-node-externals": "^3.0.4", 21 | "class-transformer": "^0.5.1", 22 | "class-validator": "^0.14.0", 23 | "dotenv": "^8.2.0", 24 | "koa": "^2.14.2", 25 | "koa-body": "^6.0.1", 26 | "koa-compress": "^5.1.1", 27 | "koa-mount": "^4.0.0", 28 | "koa-parameter": "^3.0.1", 29 | "koa-session": "^6.4.0", 30 | "koa-static": "^5.0.0", 31 | "log4js": "^6.9.1", 32 | "mysql": "^2.18.1", 33 | "routing-controllers": "^0.10.4", 34 | "typeorm": "^0.3.17" 35 | }, 36 | "devDependencies": { 37 | "@types/koa": "^2.0.48", 38 | "@types/koa-bodyparser": "^4.2.2", 39 | "@types/koa-compress": "^4.0.6", 40 | "@types/koa-session": "^6.4.2", 41 | "@types/koa-static": "^4.0.4", 42 | "@types/node": "^12.0.0", 43 | "@typescript-eslint/eslint-plugin": "^6.14.0", 44 | "clean-webpack-plugin": "^4.0.0", 45 | "cross-env": "^7.0.3", 46 | "eslint": "^8.56.0", 47 | "eslint-config-standard-with-typescript": "^43.0.0", 48 | "eslint-plugin-import": "^2.29.1", 49 | "eslint-plugin-n": "^16.4.0", 50 | "eslint-plugin-promise": "^6.1.1", 51 | "nodemon-webpack-plugin": "^4.8.2", 52 | "pm2": "^5.3.0", 53 | "ts-loader": "^9.5.1", 54 | "tsconfig-paths": "^3.8.0", 55 | "typescript": "^5.3.3", 56 | "webpack": "^5.89.0", 57 | "webpack-cli": "^5.1.4", 58 | "webpack-node-externals": "^3.0.0" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/common/help.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs' 2 | 3 | const fsPromises = fs.promises 4 | 5 | export async function createDirectory (directoryPath: string): Promise { 6 | try { 7 | await fsPromises.mkdir(directoryPath, { recursive: true }) 8 | console.log('Directory created successfully:', directoryPath) 9 | } catch (error) { 10 | console.error('Error creating directory:', error) 11 | } 12 | } 13 | 14 | export const testAlias = (): void => { 15 | console.log(123123) 16 | } 17 | -------------------------------------------------------------------------------- /src/common/interface/index.ts: -------------------------------------------------------------------------------- 1 | export enum TestEnum { 2 | A = 1, 3 | B = 2, 4 | C = 3 5 | } 6 | -------------------------------------------------------------------------------- /src/common/logger/index.ts: -------------------------------------------------------------------------------- 1 | import * as log4js from 'log4js' 2 | import * as path from 'path' 3 | import * as fs from 'fs' 4 | 5 | const logDirectory = path.join(__dirname, 'logFile') 6 | fs.mkdirSync(logDirectory, { recursive: true }) 7 | 8 | log4js.configure({ 9 | appenders: { 10 | fileAppender: { 11 | type: 'dateFile', 12 | filename: path.join(logDirectory, '~'), 13 | pattern: 'yyyy-MM-dd.log', 14 | alwaysIncludePattern: true, 15 | maxLogSize: 3 * 1024 * 1024, 16 | backups: 7 17 | }, 18 | consoleAppender: { 19 | type: 'stdout' 20 | } 21 | }, 22 | categories: { 23 | default: { appenders: ['fileAppender', 'consoleAppender'], level: 'info' } 24 | } 25 | }) 26 | 27 | const logger = log4js.getLogger() 28 | 29 | export default logger 30 | -------------------------------------------------------------------------------- /src/common/logger/logFile/~.2024-01-02.log: -------------------------------------------------------------------------------- 1 | [2024-01-02T17:17:37.806] [INFO] default - /api/record/list?aaa=111 2 | [2024-01-02T17:18:10.993] [INFO] default - /api/record/list?aaa=111 query: [Object: null prototype] { aaa: '111' } 3 | [2024-01-02T17:18:11.028] [INFO] default - /api/record/list?aaa=111 query: [Object: null prototype] { aaa: '111' } 4 | [2024-01-02T17:18:12.122] [INFO] default - /api/record/list?aaa=111 query: [Object: null prototype] { aaa: '111' } 5 | [2024-01-02T17:18:42.102] [INFO] default - /api/record/list?aaa=111 query: [Object: null prototype] { aaa: '111' } body: {} 6 | [2024-01-02T17:21:08.511] [INFO] default - /api/record/list?aaa=111 query: {"aaa":"111"} body: {} 7 | [2024-01-02T17:21:15.725] [INFO] default - /api/record/list?aaa=111 query: {"aaa":"111"} body: {} 8 | [2024-01-02T17:21:42.851] [INFO] default - /api/record/list?aaa=111 query: {"aaa":"111"} body: {} 9 | -------------------------------------------------------------------------------- /src/common/logger/logFile/~.2024-01-03.log: -------------------------------------------------------------------------------- 1 | [2024-01-03T13:34:14.269] [INFO] default - /api/record/list query: {} body: {} 2 | [2024-01-03T13:34:15.522] [INFO] default - /api/record/list query: {} body: {} 3 | [2024-01-03T13:34:16.381] [INFO] default - /api/record/list query: {} body: {} 4 | [2024-01-03T13:34:17.223] [INFO] default - /api/record/list query: {} body: {} 5 | [2024-01-03T13:38:05.074] [INFO] default - /favicon.ico query: {} body: {} 6 | [2024-01-03T13:42:10.886] [INFO] default - /api/record/list query: {} body: {} 7 | [2024-01-03T13:42:11.352] [INFO] default - /favicon.ico query: {} body: {} 8 | [2024-01-03T13:42:11.930] [INFO] default - /api/record/list query: {} body: {} 9 | [2024-01-03T14:02:01.323] [INFO] default - /api/record/list query: {} body: {} 10 | [2024-01-03T14:02:02.637] [INFO] default - /api/record/list query: {} body: {} 11 | [2024-01-03T14:05:25.835] [INFO] default - /api/record/list query: {} body: {} 12 | [2024-01-03T14:05:26.755] [INFO] default - /api/record/list query: {} body: {} 13 | [2024-01-03T14:21:36.127] [INFO] default - /api/record/list query: {} body: {} 14 | [2024-01-03T14:21:37.632] [INFO] default - /api/record/list query: {} body: {} 15 | [2024-01-03T14:22:07.400] [INFO] default - /api/record/list query: {} body: {} 16 | [2024-01-03T14:22:08.554] [INFO] default - /api/record/list query: {} body: {} 17 | [2024-01-03T14:22:09.458] [INFO] default - /api/record/list query: {} body: {} 18 | [2024-01-03T14:22:10.422] [INFO] default - /api/record/list query: {} body: {} 19 | [2024-01-03T14:22:11.258] [INFO] default - /api/record/list query: {} body: {} 20 | [2024-01-03T14:22:11.994] [INFO] default - /api/record/list query: {} body: {} 21 | [2024-01-03T14:22:14.374] [INFO] default - /api/record/list query: {} body: {} 22 | [2024-01-03T14:22:15.047] [INFO] default - /api/record/list query: {} body: {} 23 | [2024-01-03T14:22:22.184] [INFO] default - /api/record/list query: {} body: {} 24 | [2024-01-03T14:22:23.003] [INFO] default - /api/record/list query: {} body: {} 25 | [2024-01-03T14:23:22.934] [INFO] default - /api/record/list query: {} body: {} 26 | [2024-01-03T14:23:26.234] [INFO] default - /api/record/post query: {} body: {"test":12312322123,"mode":12312312} 27 | [2024-01-03T14:23:28.587] [INFO] default - /api/record/list query: {} body: {} 28 | [2024-01-03T14:23:29.502] [INFO] default - /api/record/list query: {} body: {} 29 | [2024-01-03T14:23:30.393] [INFO] default - /api/record/list query: {} body: {} 30 | [2024-01-03T14:23:47.856] [INFO] default - /api/record/post query: {} body: {"test":12312123,"mode":1231212} 31 | [2024-01-03T14:23:49.588] [INFO] default - /api/record/list query: {} body: {} 32 | [2024-01-03T14:23:50.620] [INFO] default - /api/record/list query: {} body: {} 33 | [2024-01-03T14:23:51.365] [INFO] default - /api/record/list query: {} body: {} 34 | [2024-01-03T14:23:59.389] [INFO] default - /api/record/post query: {} body: {"test":123121213,"mode":123121211} 35 | [2024-01-03T14:24:00.846] [INFO] default - /api/record/list query: {} body: {} 36 | [2024-01-03T14:24:01.827] [INFO] default - /api/record/list query: {} body: {} 37 | [2024-01-03T14:24:02.528] [INFO] default - /api/record/list query: {} body: {} 38 | [2024-01-03T14:24:03.116] [INFO] default - /api/record/list query: {} body: {} 39 | [2024-01-03T14:24:03.591] [INFO] default - /api/record/list query: {} body: {} 40 | [2024-01-03T14:24:04.104] [INFO] default - /api/record/list query: {} body: {} 41 | [2024-01-03T14:24:27.111] [INFO] default - /api/record/post query: {} body: {"test":123121213,"mode":123121222211} 42 | [2024-01-03T14:24:28.397] [INFO] default - /api/record/list query: {} body: {} 43 | [2024-01-03T14:24:29.287] [INFO] default - /api/record/list query: {} body: {} 44 | [2024-01-03T14:25:32.808] [INFO] default - /api/record/list query: {} body: {} 45 | [2024-01-03T14:25:33.969] [INFO] default - /api/record/list query: {} body: {} 46 | [2024-01-03T14:26:36.932] [INFO] default - /api/record/list query: {} body: {} 47 | -------------------------------------------------------------------------------- /src/common/static/appA/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 8 | 9 | hello koa base appA 10 | 11 | -------------------------------------------------------------------------------- /src/common/static/appB/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 8 | 9 | hello koa base appB 10 | 11 | -------------------------------------------------------------------------------- /src/config.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path' 2 | import * as dotenv from 'dotenv' 3 | 4 | dotenv.config() 5 | 6 | export const { PORT } = process.env 7 | 8 | export const SESSION_CONFIG = { 9 | key: 'base_session_key', 10 | maxAge: 4 * 60 * 60 * 1000, 11 | httpOnly: true, 12 | signed: true, 13 | rolling: true, 14 | renew: false 15 | } 16 | 17 | export const appApath = path.join(__dirname, './common/static/appA') 18 | export const appBpath = path.join(__dirname, './common/static/appB') 19 | -------------------------------------------------------------------------------- /src/controllers/activeRecord.ts: -------------------------------------------------------------------------------- 1 | import { JsonController, Get, QueryParams, Post, Body } from 'routing-controllers' 2 | import activeRecord from '../entity/activeRecord' 3 | 4 | @JsonController('/record') 5 | export class ActiveRecordController { 6 | @Get('/list') 7 | async list (@QueryParams() query: string): Promise { 8 | console.log(query, '请求的query参数') 9 | const data = await activeRecord.find() 10 | return data 11 | } 12 | 13 | @Post('/post') 14 | async post (@Body() body: any): Promise { 15 | console.log(body, '请求的body参数') 16 | const newLine = activeRecord.create({ 17 | mode: body.mode, 18 | test: body.test 19 | }) 20 | const data = await activeRecord.save(newLine) 21 | return data 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/controllers/dataMapper.ts: -------------------------------------------------------------------------------- 1 | import { JsonController, Get, QueryParams, Post, Body, UseBefore } from 'routing-controllers' 2 | import AppDataSource from '../entity' 3 | import dataMapper from '../entity/dataMapper' 4 | import { UseLoginWare } from '../middleware' 5 | 6 | @JsonController('/test') 7 | export class DataMapperController { 8 | dataMapper = AppDataSource.getRepository(dataMapper) 9 | 10 | @Get('/list') 11 | @UseBefore(UseLoginWare) 12 | async list (@QueryParams() query: string): Promise { 13 | console.log(query, '请求的query参数') 14 | const data = await this.dataMapper.find() 15 | // const data = await AppDataSource.manager.save(dataMapper) 或者这样可以更灵活可以配合多个实体 16 | return data 17 | } 18 | 19 | @Post('/post') 20 | async post (@Body() body: any): Promise { 21 | console.log(body, '请求的body参数') 22 | const newLine = this.dataMapper.create({ 23 | username: body.username, 24 | password: body.password 25 | }) 26 | const data = await this.dataMapper.save(newLine) 27 | return data 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/entity/activeRecord.ts: -------------------------------------------------------------------------------- 1 | import { Entity, Column, PrimaryColumn, BaseEntity } from 'typeorm' 2 | 3 | // active-record 实体模式 https://www.typeorm.org/#%E5%88%9B%E5%BB%BA%E6%A8%A1%E5%9E%8B 4 | @Entity('active_record') 5 | export default class ActiveRecord extends BaseEntity { 6 | @PrimaryColumn() 7 | mode: string 8 | 9 | @Column() 10 | test: string 11 | } 12 | -------------------------------------------------------------------------------- /src/entity/dataMapper.ts: -------------------------------------------------------------------------------- 1 | import { Entity, Column, PrimaryColumn } from 'typeorm' 2 | 3 | // data-mapper 实体模式 https://www.typeorm.org/#%E5%88%9B%E5%BB%BA%E6%A8%A1%E5%9E%8B 4 | @Entity('data_mapper') 5 | export default class DataMapper { 6 | @PrimaryColumn() 7 | username: string 8 | 9 | @Column() 10 | password: string 11 | } 12 | -------------------------------------------------------------------------------- /src/entity/index.ts: -------------------------------------------------------------------------------- 1 | import { DataSource } from 'typeorm' 2 | import dataMapper from './dataMapper' 3 | import activeRecord from './activeRecord' 4 | 5 | const AppDataSource = new DataSource({ 6 | type: 'mysql', 7 | host: 'xxxxxx', 8 | port: 3306, 9 | username: 'root', 10 | password: 'xxxxxx', 11 | database: 'test', 12 | synchronize: true, 13 | logging: false, 14 | entities: [dataMapper, activeRecord], 15 | subscribers: [], 16 | migrations: [] 17 | }) 18 | 19 | export default AppDataSource 20 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import 'reflect-metadata' 2 | import * as Koa from 'koa' 3 | import { koaBody } from 'koa-body' 4 | import * as serve from 'koa-static' 5 | import * as mount from 'koa-mount' 6 | import * as KoaSession from 'koa-session' 7 | import * as KoaParameter from 'koa-parameter' 8 | import { useKoaServer } from 'routing-controllers' 9 | import { DataMapperController } from './controllers/dataMapper' 10 | import { ActiveRecordController } from './controllers/activeRecord' 11 | import { PORT, SESSION_CONFIG, appApath, appBpath } from './config' 12 | import { UseLogWare } from './middleware' 13 | import { testAlias } from '@common/help' 14 | import AppDataSource from './entity' 15 | 16 | testAlias() 17 | 18 | const app: Koa = new Koa() 19 | 20 | app.keys = ['session base secret'] 21 | 22 | app.use(koaBody()) 23 | app.use(KoaParameter(app) as Koa.Middleware) 24 | app.use(KoaSession(SESSION_CONFIG, app)) 25 | 26 | // 多个静态资源路径 27 | app.use(mount('/appA', serve(appApath))) 28 | app.use(mount('/appB', serve(appBpath))) 29 | 30 | AppDataSource.initialize() 31 | useKoaServer(app, { 32 | controllers: [DataMapperController, ActiveRecordController], 33 | middlewares: [UseLogWare], 34 | routePrefix: '/api' 35 | }) 36 | 37 | app.listen(PORT) 38 | console.log('Service Started Successfully') 39 | -------------------------------------------------------------------------------- /src/middleware/index.ts: -------------------------------------------------------------------------------- 1 | import { type Context } from 'koa' 2 | import { Middleware, type KoaMiddlewareInterface } from 'routing-controllers' 3 | import logger from '../common/logger' 4 | 5 | /** 6 | * 用于鉴权的中间价或者使用官方推荐的@Authorized() 7 | * 更多的学习这里 https://github.com/typestack/routing-controllers 8 | */ 9 | export class UseLoginWare implements KoaMiddlewareInterface { 10 | async use (ctx: Context, next: () => Promise): Promise { 11 | try { 12 | if (!ctx.session?.account) { 13 | ctx.status = 401 14 | ctx.body = '没有权限,等登陆' 15 | return 16 | } 17 | await next() 18 | } catch (error) { 19 | ctx.status = error.httpCode || 500 20 | ctx.body = { 21 | message: error.message || '内部服务器错误' 22 | } 23 | } 24 | } 25 | } 26 | 27 | @Middleware({ type: 'after' }) 28 | export class UseLogWare implements KoaMiddlewareInterface { 29 | async use (ctx: Context, next: () => Promise): Promise { 30 | try { 31 | logger.info(ctx.request.url, 'query:', JSON.stringify(ctx.query), 'body:', JSON.stringify(ctx.request.body)) 32 | await next() 33 | } catch (error) { 34 | console.log(error) 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/types/koa-parameter.d.ts: -------------------------------------------------------------------------------- 1 | import type Koa, { Middleware } from 'koa' 2 | 3 | type ParameterRules = Record 4 | 5 | declare module 'koa-parameter' { 6 | type translate = (args: any[]) => string 7 | function parameter (app: Koa, translate?: translate): Middleware 8 | export = parameter 9 | } 10 | 11 | declare module 'koa' { 12 | interface Context { 13 | verifyParams: (rules: ParameterRules) => void 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": false, 5 | "removeComments": true, 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "target": "es6", 9 | "sourceMap": false, 10 | "outDir": "./dist", 11 | "baseUrl": "./", 12 | "paths": { 13 | "@common/*": [ 14 | "./src/common/*" 15 | ], 16 | }, 17 | "strictNullChecks": true, 18 | "skipLibCheck": true, 19 | "typeRoots": [ 20 | "./node_modules/@types", 21 | "./src/types" 22 | ] 23 | }, 24 | "exclude": [ 25 | "node_modules", 26 | "./build" 27 | ] 28 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const NodeExternals = require('webpack-node-externals') 3 | const { CleanWebpackPlugin } = require('clean-webpack-plugin') 4 | const NodemonPlugin = require('nodemon-webpack-plugin') 5 | 6 | const mode = process.env.NODE_ENV 7 | 8 | module.exports = { 9 | mode, 10 | entry: './src/index.ts', 11 | target: 'node', 12 | output: { 13 | path: path.resolve(__dirname, 'dist'), 14 | filename: 'index.js' 15 | }, 16 | externals: [NodeExternals()], 17 | resolve: { 18 | extensions: ['.ts', '.js'], 19 | alias: { 20 | '@common': path.resolve(__dirname, 'src/common') 21 | } 22 | }, 23 | optimization: { 24 | // Node.js 中无需压缩代码,可以关闭以提高构建性能 25 | minimize: false 26 | }, 27 | node: { 28 | __dirname: true, 29 | __filename: true 30 | }, 31 | module: { 32 | rules: [ 33 | { test: /\.ts$/, loader: 'ts-loader', exclude: /node_modules/ } 34 | ] 35 | }, 36 | plugins: [ 37 | new CleanWebpackPlugin(), 38 | new NodemonPlugin({ 39 | watch: path.resolve(__dirname, 'src'), 40 | ext: 'ts' 41 | }) 42 | ] 43 | } 44 | --------------------------------------------------------------------------------