File size: 3,847 Bytes
ba2906a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import fs from 'node:fs'
import path from 'node:path'
import YAML from 'yaml'
import multer from 'multer'
import rateLimit from 'express-rate-limit'

export default class plugin {
  /**
   * @param {string} name 插件名称
   * @param {string} [dsc] 插件描述
   * @param {number} [priority] 插件优先级
   * @param {string} [route] 请求路径前缀
   * @param {string} rule.method 请求方法
   * * rule.method可以是数组,method[ws]属于自定义方法不经过app.handle所以不支持通配符
   * @param {string} rule.path 请求路径
   * * 有route前缀时请求路径=route+rule.path
   * @param {string} [rule.use] 中间件路由
   * * 匹配到的路由会在rule.fnc方法之前按顺序执行一遍rule.use中间件
   * * rule.use是string时fn需要返回中间件函数, rule.use是数组时则直接传入req,res对象
   * @param {string} rule.fnc 请求默认方法
   * * 每个请求都是一个实例,可以打印this查看默认对象
   * * 方法执行完成之前必须结束与客户端的连接
   * * 或者调用next()将控制权交给下一个路由,否则客户端请求会一直处于挂起状态
   */
  constructor (data = {}) {
    /** 插件名称 */
    this.name = data.name || ''
    /** 插件描述 */
    this.dsc = data.dsc || ''
    /** 插件优先级 */
    this.priority = data.priority || 5
    /** 请求路径前缀 */
    this.route = data.route || ''
    /** 路由规则 */
    this.rule = data.rule || []
    /** 定时任务,可以是数组 */
    this.task = {
      /** 任务名 */
      name: '',
      /** 任务方法名 */
      fnc: data.task?.fnc || '',
      /** 任务cron表达式 */
      cron: data.task?.cron || ''
    }
  }

  /** 读json */
  readJson (filePath, format = 'json') {
    try {
      if (format == 'yaml') {
        return YAML.parse(fs.readFileSync(filePath, 'utf8'))
      }
      return JSON.parse(fs.readFileSync(filePath, 'utf8'))
    } catch (err) {
      return false
    }
  }

  /** 写json */
  writeJson (savePath, data, format = 'json') {
    this.mkdir(path.dirname(savePath))
    if (format == 'yaml') return fs.writeFileSync(savePath, YAML.stringify(data))
    return fs.writeFileSync(savePath, JSON.stringify(data, null, 2))
  }

  /** 创建文件夹 */
  mkdir (dirname) {
    if (fs.existsSync(dirname)) {
      return true
    } else {
      if (this.mkdir(path.dirname(dirname))) {
        fs.mkdirSync(dirname)
        return true
      }
    }
  }

  /** 休眠函数 */
  sleep (ms) {
    return new Promise((resolve) => setTimeout(resolve, ms))
  }

  /** 速率限制 */
  limiter (limit = 15, cd = 60, options = {}) {
    options = {
      limit,
      windowMs: cd * 1000,
      legacyHeaders: false,
      message: { status: 1, message: 'Frequent requests, please try again later' },
      ...options
    }
    return rateLimit(options)
  }

  /** 文件上传, single, array, any */
  upload (savaPath = './public/upload', filename, options = {}) {
    options = {
      storage: multer.diskStorage({
        destination: (req, file, cb) => {
          cb(null, savaPath)
        },
        filename: filename || ((req, file, cb) => {
          cb(null, Date.now() + path.extname(file.originalname))
        })
      }),
      limits: { fileSize: 5 * 1024 * 1024, files: 1 },
      ...options
    }
    return multer(options)
  }

  /** CORS头部 */
  cors () {
    return (req, res, next) => {
      res.set({
        'Access-Control-Allow-Credentials': true,
        'Access-Control-Max-Age': 1728000,
        'Access-Control-Allow-Origin': req.headers.origin || '*',
        'Access-Control-Allow-Headers': 'X-Requested-With,Content-Type',
        'Access-Control-Allow-Methods': 'PUT,POST,GET,DELETE,OPTIONS'
      })
      req.method === 'OPTIONS' ? res.status(204).end() : next()
    }
  }
}