node-karin 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +57 -0
  3. package/config/defSet/App.yaml +37 -0
  4. package/config/defSet/config.yaml +43 -0
  5. package/config/defSet/group.yaml +18 -0
  6. package/config/defSet/pm2.yaml +21 -0
  7. package/config/defSet/redis.yaml +18 -0
  8. package/config/defSet/server.yaml +42 -0
  9. package/config/view/App.yaml +74 -0
  10. package/config/view/config.yaml +100 -0
  11. package/config/view/group.yaml +62 -0
  12. package/config/view/pm2.yaml +41 -0
  13. package/config/view/redis.yaml +25 -0
  14. package/config/view/server.yaml +93 -0
  15. package/lib/adapter/onebot/onebot11.d.ts +430 -0
  16. package/lib/adapter/onebot/onebot11.js +1302 -0
  17. package/lib/core/init.d.ts +0 -0
  18. package/lib/core/init.js +4 -0
  19. package/lib/core/karin.d.ts +72 -0
  20. package/lib/core/karin.js +51 -0
  21. package/lib/core/listener.d.ts +121 -0
  22. package/lib/core/listener.js +188 -0
  23. package/lib/core/plugin.app.d.ts +15 -0
  24. package/lib/core/plugin.app.js +18 -0
  25. package/lib/core/plugin.d.ts +182 -0
  26. package/lib/core/plugin.js +138 -0
  27. package/lib/core/plugin.loader.d.ts +149 -0
  28. package/lib/core/plugin.loader.js +462 -0
  29. package/lib/core/server.d.ts +26 -0
  30. package/lib/core/server.js +213 -0
  31. package/lib/db/level.d.ts +20 -0
  32. package/lib/db/level.js +38 -0
  33. package/lib/db/redis.d.ts +41 -0
  34. package/lib/db/redis.js +137 -0
  35. package/lib/db/redis_level.d.ts +113 -0
  36. package/lib/db/redis_level.js +290 -0
  37. package/lib/event/event.d.ts +138 -0
  38. package/lib/event/event.handler.d.ts +29 -0
  39. package/lib/event/event.handler.js +142 -0
  40. package/lib/event/event.js +120 -0
  41. package/lib/event/message.d.ts +102 -0
  42. package/lib/event/message.handler.d.ts +25 -0
  43. package/lib/event/message.handler.js +240 -0
  44. package/lib/event/message.js +70 -0
  45. package/lib/event/notice.d.ts +49 -0
  46. package/lib/event/notice.js +15 -0
  47. package/lib/event/request.d.ts +49 -0
  48. package/lib/event/request.js +15 -0
  49. package/lib/event/review.handler.d.ts +54 -0
  50. package/lib/event/review.handler.js +382 -0
  51. package/lib/index.d.ts +23 -0
  52. package/lib/index.js +40 -0
  53. package/lib/renderer/app.d.ts +53 -0
  54. package/lib/renderer/app.js +93 -0
  55. package/lib/renderer/base.d.ts +30 -0
  56. package/lib/renderer/base.js +71 -0
  57. package/lib/renderer/client.d.ts +30 -0
  58. package/lib/renderer/client.js +159 -0
  59. package/lib/renderer/http.d.ts +19 -0
  60. package/lib/renderer/http.js +51 -0
  61. package/lib/renderer/server.d.ts +42 -0
  62. package/lib/renderer/server.js +112 -0
  63. package/lib/renderer/wormhole.d.ts +1 -0
  64. package/lib/renderer/wormhole.js +154 -0
  65. package/lib/types/adapter.d.ts +575 -0
  66. package/lib/types/adapter.js +1 -0
  67. package/lib/types/config.d.ts +327 -0
  68. package/lib/types/config.js +1 -0
  69. package/lib/types/element.d.ts +576 -0
  70. package/lib/types/element.js +1 -0
  71. package/lib/types/index.d.ts +8 -0
  72. package/lib/types/index.js +8 -0
  73. package/lib/types/logger.d.ts +109 -0
  74. package/lib/types/logger.js +1 -0
  75. package/lib/types/onebots11.d.ts +1371 -0
  76. package/lib/types/onebots11.js +1 -0
  77. package/lib/types/plugin.d.ts +282 -0
  78. package/lib/types/plugin.js +1 -0
  79. package/lib/types/render.d.ts +111 -0
  80. package/lib/types/render.js +1 -0
  81. package/lib/types/reply.d.ts +40 -0
  82. package/lib/types/reply.js +1 -0
  83. package/lib/types/types.d.ts +898 -0
  84. package/lib/types/types.js +1 -0
  85. package/lib/utils/YamlEditor.d.ts +62 -0
  86. package/lib/utils/YamlEditor.js +208 -0
  87. package/lib/utils/button.d.ts +49 -0
  88. package/lib/utils/button.js +79 -0
  89. package/lib/utils/common.d.ts +123 -0
  90. package/lib/utils/common.js +413 -0
  91. package/lib/utils/config.d.ts +72 -0
  92. package/lib/utils/config.js +254 -0
  93. package/lib/utils/exec.d.ts +22 -0
  94. package/lib/utils/exec.js +36 -0
  95. package/lib/utils/ffmpeg.d.ts +12 -0
  96. package/lib/utils/ffmpeg.js +25 -0
  97. package/lib/utils/handler.d.ts +76 -0
  98. package/lib/utils/handler.js +102 -0
  99. package/lib/utils/logger.d.ts +3 -0
  100. package/lib/utils/logger.js +104 -0
  101. package/lib/utils/segment.d.ts +276 -0
  102. package/lib/utils/segment.js +448 -0
  103. package/lib/utils/update.d.ts +69 -0
  104. package/lib/utils/update.js +151 -0
  105. package/lib/utils/updateVersion.d.ts +33 -0
  106. package/lib/utils/updateVersion.js +145 -0
  107. package/package.json +92 -0
@@ -0,0 +1,462 @@
1
+ import fs from 'fs'
2
+ import lodash from 'lodash'
3
+ import path from 'path'
4
+ import schedule from 'node-schedule'
5
+ import Renderer from '../renderer/app.js'
6
+ import button from '../utils/button.js'
7
+ import Common from '../utils/common.js'
8
+ import handler from '../utils/handler.js'
9
+ import listener from './listener.js'
10
+ import logger from '../utils/logger.js'
11
+ import PluginApp from './plugin.app.js'
12
+ /**
13
+ * 加载插件
14
+ */
15
+ export default new (class PluginLoader {
16
+ dir
17
+ dirPath
18
+ /**
19
+ * - 插件列表
20
+ */
21
+ Apps
22
+ /**
23
+ * - 定时任务
24
+ */
25
+ task
26
+ /**
27
+ * - 监听器
28
+ */
29
+ watcher
30
+ /**
31
+ * - 热更新列表
32
+ */
33
+ watchList
34
+ /**
35
+ * - 插件索引ID
36
+ */
37
+ index
38
+ /**
39
+ * - 命令插件索引列表
40
+ */
41
+ rule
42
+ PluginList
43
+ constructor () {
44
+ this.dir = './plugins'
45
+ this.dirPath = Common.urlToPath(import.meta.url)
46
+ this.Apps = []
47
+ this.task = []
48
+ this.watcher = new Map()
49
+ /** 热更新收集 */
50
+ this.watchList = []
51
+ this.rule = []
52
+ this.index = 0
53
+ this.PluginList = {}
54
+ }
55
+
56
+ /**
57
+ * 插件初始化
58
+ */
59
+ async load () {
60
+ const files = this.getPlugins()
61
+ listener.once('plugin.loader', () => {
62
+ for (const v of this.watchList) {
63
+ v.name ? this.watch(v.dir, v.name) : this.watchDir(v.dir)
64
+ }
65
+ })
66
+ logger.info(logger.green('-----------'))
67
+ logger.info('加载插件中..')
68
+ /** 载入插件 */
69
+ const promises = files.map(async ({ dir, name }) => await this.createdApp(dir, name, false))
70
+ /** 等待所有插件加载完成 */
71
+ await Promise.all(promises)
72
+ const handlerKeys = Object.keys(handler.events)
73
+ let handlerCount = 0
74
+ handlerKeys.forEach((key) => {
75
+ handlerCount += handler.events[key].length
76
+ })
77
+ logger.info(`[按钮][${button.Apps.length}个] 加载完成`)
78
+ logger.info(`[插件][${this.Apps.length}个] 加载完成`)
79
+ logger.info(`[渲染器][${Renderer.Apps.length}个] 加载完成`)
80
+ logger.info(`[定时任务][${this.task.length}个] 加载完成`)
81
+ logger.info(`[Handler][Key:${handlerKeys.length}个][fnc:${handlerCount}个] 加载完成`)
82
+ logger.info(logger.green('-----------'))
83
+ logger.info(`Karin启动完成:耗时 ${logger.green(process.uptime().toFixed(2))} 秒...`)
84
+ /** 优先级排序 */
85
+ this.orderBy()
86
+ listener.emit('plugin.loader')
87
+ return this
88
+ }
89
+
90
+ /**
91
+ * 获取所有插件
92
+ */
93
+ getPlugins () {
94
+ const Apps = []
95
+ /** 获取所有插件包 */
96
+ const plugins = Common.getPlugins()
97
+ const isTs = process.env.karin_app_lang === 'ts'
98
+ for (const dir of plugins) {
99
+ /**
100
+ * - 插件包路径
101
+ * - 例如: ./plugins/karin-plugin-example
102
+ */
103
+ const PluginPath = `${this.dir}/${dir}`
104
+ // 非插件包
105
+ if (!Common.isPlugin(PluginPath)) {
106
+ const list = fs.readdirSync(`${this.dir}/${dir}`, {
107
+ withFileTypes: true,
108
+ })
109
+ for (const file of list) {
110
+ /** 忽略不符合规则的文件 */
111
+ const ext = isTs ? ['.js', '.ts'] : ['.js']
112
+ if (!ext.includes(path.extname(file.name))) { continue }
113
+ Apps.push({ dir, name: file.name })
114
+ }
115
+ }
116
+ /** js环境 */
117
+ if (Common.exists(`${PluginPath}/index.js`)) {
118
+ Apps.push({ dir, name: 'index.js' })
119
+ }
120
+ const appList = ['apps', 'dist/apps']
121
+ appList.forEach(app => {
122
+ if (Common.isDir(`${PluginPath}/${app}`)) {
123
+ const result = this.getApps((`${dir}/${app}`))
124
+ Apps.push(...result)
125
+ }
126
+ })
127
+ /** ts环境 */
128
+ if (isTs) {
129
+ if (Common.exists(`${PluginPath}/index.ts`)) {
130
+ Apps.push({ dir, name: 'index.ts' })
131
+ }
132
+ if (Common.isDir(`${PluginPath}/src/apps`)) {
133
+ const result = this.getApps((`${dir}/src/apps`), true)
134
+ Apps.push(...result)
135
+ }
136
+ }
137
+ }
138
+ return Apps
139
+ }
140
+
141
+ /**
142
+ * 获取指定文件夹下的所有插件
143
+ * @param dir - 插件包名称
144
+ * @param isTs - 是否获取ts插件
145
+ */
146
+ getApps (dir, isTs = false) {
147
+ const info = []
148
+ const ext = isTs ? ['.js', '.ts'] : ['.js']
149
+ const list = fs.readdirSync(`${this.dir}/${dir}`, { withFileTypes: true })
150
+ for (const file of list) {
151
+ // 获取后缀
152
+ const extname = path.extname(file.name)
153
+ if (!ext.includes(extname)) { continue }
154
+ info.push({ dir, name: file.name })
155
+ }
156
+ return info
157
+ }
158
+
159
+ /**
160
+ * 构建插件信息
161
+ */
162
+ App (
163
+ /**
164
+ * - 插件Class 未实例化
165
+ */
166
+ App,
167
+ /**
168
+ * - 插件实例化后的Class
169
+ */
170
+ Class,
171
+ /**
172
+ * - 插件文件信息
173
+ */
174
+ file) {
175
+ const info = {
176
+ App,
177
+ /** 插件文件信息 */
178
+ file,
179
+ name: Class.name,
180
+ desc: Class.dsc || Class.desc || Class.name,
181
+ event: Class.event || 'message',
182
+ priority: Class.priority ?? 5000,
183
+ accept: typeof Class.accept === 'function',
184
+ rule: Class.rule || [],
185
+ type: typeof App === 'function' ? 'function' : 'class',
186
+ task: Class.task || [],
187
+ handler: Class.handler || [],
188
+ button: Class.button || [],
189
+ }
190
+ /** 进一步处理rule */
191
+ info.rule.forEach((val, index) => {
192
+ info.rule[index].reg = new RegExp(val.reg)
193
+ info.rule[index].log = val.log === false ? (id, log) => logger.debug('mark', id, log) : (id, log) => logger.bot('mark', id, log)
194
+ })
195
+ return info
196
+ }
197
+
198
+ /** 排序 */
199
+ orderBy () {
200
+ const list = []
201
+ Object.keys(this.PluginList).forEach(key => {
202
+ list.push({ key, val: this.PluginList[key].priority })
203
+ })
204
+ this.rule = lodash.orderBy(list, ['val'], ['asc']).map((v) => Number(v.key))
205
+ logger.debug('rule排序完成...')
206
+ logger.info(this.rule)
207
+ }
208
+
209
+ /**
210
+ * 新增插件
211
+ * @param dir - 插件包路径
212
+ * @param name - 插件名称
213
+ * @param isOrderBy - 是否重新导入
214
+ */
215
+ async createdApp (dir, name, isOrderBy = false) {
216
+ try {
217
+ let path = `${this.dirPath}plugins/${dir}/${name}`
218
+ if (isOrderBy) { path = path + `?${Date.now()}` }
219
+ const tmp = await import(path)
220
+ lodash.forEach(tmp, (App) => {
221
+ const index = this.index
222
+ this.index++
223
+ if (typeof App === 'object') {
224
+ if (App?.file?.type !== 'function') { return }
225
+ if (!App?.name) { return logger.error(`[${dir}][${name}] 插件名称错误`) }
226
+ App.file.dir = dir
227
+ App.file.name = name
228
+ this.PluginList[index] = App
229
+ }
230
+ if (typeof App !== 'function' || !App?.prototype?.constructor) { return }
231
+ const Class = new App()
232
+ if (!Class.name) { return logger.error(`[${dir}][${name}] 插件名称错误`) }
233
+ logger.debug(`载入插件 [${name}][${Class.name}]`)
234
+ const info = PluginApp({
235
+ file: {
236
+ dir,
237
+ name,
238
+ type: 'class',
239
+ fnc: App,
240
+ },
241
+ name: Class.name,
242
+ event: Class.event,
243
+ priority: Class.priority,
244
+ accept: Class.accept && typeof Class.accept === 'function',
245
+ })
246
+ /** 定时任务 */
247
+ lodash.forEach(Class.task, (val) => {
248
+ if (!val.name) { return logger.error(`[${dir}][${name}] 定时任务name错误`) }
249
+ if (!val.cron) { return logger.error(`[${dir}][${name}] 定时任务cron错误:${Class.name}`) }
250
+ info.task.push({
251
+ name: val.name,
252
+ cron: val.cron,
253
+ fnc: val.fnc,
254
+ log: val.log === false ? (log) => logger.debug(log) : (log) => logger.mark(log),
255
+ schedule: schedule.scheduleJob(val.cron, async () => {
256
+ try {
257
+ typeof val.log === 'function' && val.log(`[定时任务][${dir}][${val.name}] 开始执行`)
258
+ if (typeof val.fnc === 'function') {
259
+ await val.fnc()
260
+ } else {
261
+ const cla = new App()
262
+ await cla[val.fnc]()
263
+ }
264
+ typeof val.log === 'function' && val.log(`[定时任务][${dir}][${val.name}] 执行完毕`)
265
+ } catch (error) {
266
+ logger.error(`[定时任务][${dir}][${val.name}] 执行报错`)
267
+ logger.error(error)
268
+ }
269
+ }),
270
+ })
271
+ })
272
+ /** rule */
273
+ lodash.forEach(Class.rule, (val) => {
274
+ if (!val.fnc) { return logger.error(`[${dir}][${name}] rule.fnc错误:${Class.name}`) }
275
+ info.rule.push({
276
+ reg: val.reg instanceof RegExp ? val.reg : new RegExp(val.reg),
277
+ fnc: val.fnc,
278
+ event: val.event,
279
+ permission: val.permission || 'all',
280
+ log: val.log === false ? (id, log) => logger.debug('mark', id, log) : (id, log) => logger.bot('mark', id, log),
281
+ })
282
+ })
283
+ /** 执行初始化 */
284
+ Class.init && Class.init()
285
+ this.PluginList[index] = info
286
+ // const Class = new App()
287
+ /** 注册Handler */
288
+ // if (!lodash.isEmpty(Class.handler)) handler.add({ name, dir, App, Class })
289
+ /** 注册按钮 */
290
+ // if (!lodash.isEmpty(Class.button)) button.add({ name, dir, App, Class })
291
+ /** 收集插件 */
292
+ // const res = this.App(App, Class, { name, dir, path: `${dir}/${name}` })
293
+ // this.Apps.push(res)
294
+ return true
295
+ })
296
+ // rule收集并排序
297
+ if (isOrderBy) { this.orderBy() }
298
+ return true
299
+ } catch (error) {
300
+ if (/Cannot find package '(.+?)'/.exec(error)?.[1]) {
301
+ const pack = /Cannot find package '(.+?)'/.exec(error)?.[1] || ''
302
+ logger.error(logger.red('--------插件载入错误--------'))
303
+ logger.mark(`错误: [${dir}][${name}] 缺少必要的依赖项: ${logger.red(pack)}`)
304
+ logger.mark(`操作:请尝试在命令终端中执行 ${logger.red('pnpm i -P')} 命令安装依赖项`)
305
+ logger.mark('提示:如安装后仍未解决,可选择以下方案')
306
+ logger.mark(` 1.手工安装依赖: ${logger.red('pnpm i ' + pack)}`)
307
+ logger.mark(` 2.联系插件作者:联系插件作者将 ${logger.red(pack)} 依赖项添加至插件的packageon文件中的dependencies字段中`)
308
+ logger.error(logger.red('-----------------------------'))
309
+ } else {
310
+ logger.error(`载入插件错误:${logger.red(`${dir}/${name}`)}`)
311
+ logger.error(error)
312
+ }
313
+ return false
314
+ }
315
+ }
316
+
317
+ /**
318
+ * 卸载插件
319
+ */
320
+ uninstallApp (dir, name) {
321
+ this.Apps = this.Apps.filter((v) => !(v.file.dir === dir && v.file.name === name))
322
+ this.uninstallTask(dir, name)
323
+ button.del(dir, name)
324
+ handler.del({ dir, name, key: '' })
325
+ }
326
+
327
+ /** 创建定时任务 */
328
+ async creatTask () {
329
+ this.task.forEach((val, index) => {
330
+ if (val.schedule) { return }
331
+ val.log = val.log === false ? (log) => logger.debug(log) : (log) => logger.mark(log)
332
+ val.schedule = schedule.scheduleJob(val.cron, async () => {
333
+ try {
334
+ typeof val.log === 'function' && val.log(`[定时任务][${val.file.dir}][${val.name}] 开始执行`)
335
+ const App = new val.App()
336
+ await App[val.fnc]()
337
+ typeof val.log === 'function' && val.log(`[定时任务][${val.file.dir}][${val.name}] 执行完毕`)
338
+ } catch (error) {
339
+ logger.error(`[定时任务][${val.file.dir}][${val.name}] 执行报错`)
340
+ logger.error(error)
341
+ }
342
+ })
343
+ this.task[index] = val
344
+ })
345
+ }
346
+
347
+ /**
348
+ * 卸载定时任务
349
+ */
350
+ uninstallTask (dir, name) {
351
+ this.task = this.task.filter((task) => {
352
+ if (task.file.dir === dir && task.file.name === name) {
353
+ /** 停止定时任务 */
354
+ task.schedule && task.schedule.cancel()
355
+ /** 移除定时任务 */
356
+ return false
357
+ }
358
+ /** 保留定时任务 */
359
+ return true
360
+ })
361
+ }
362
+
363
+ /**
364
+ * 监听单个文件热更新
365
+ */
366
+ watch (dir, name) {
367
+ // if (this.watcher.get(`${dir}.${name}`)) return
368
+ // const file = `./plugins/${dir}/${name}`
369
+ // const watcher = chokidar.watch(file)
370
+ // /** 监听修改 */
371
+ // watcher.on('change', async () => {
372
+ // /** 卸载 */
373
+ // this.uninstallApp(dir, name)
374
+ // /** 载入插件 */
375
+ // const res = await this.createdApp(dir, name, true)
376
+ // if (!res) return
377
+ // logger.mark(`[修改插件][${dir}][${name}]`)
378
+ // })
379
+ /** 监听删除 */
380
+ // watcher.on('unlink', async () => {
381
+ // /** 卸载 */
382
+ // this.uninstallApp(dir, name)
383
+ // this.watcher.delete(`${dir}.${name}`)
384
+ // logger.mark(`[卸载插件][${dir}][${name}]`)
385
+ // })
386
+ // this.watcher.set(`${dir}.${name}`, watcher)
387
+ }
388
+
389
+ /**
390
+ * 监听文件夹更新
391
+ */
392
+ watchDir (dir) {
393
+ // if (dir) return
394
+ // if (this.watcher.get(dir)) return
395
+ // const file = `${this.dir}/${dir}/`
396
+ // const watcher = chokidar.watch(file)
397
+ // /** 热更新 */
398
+ // setTimeout(() => {
399
+ // /** 新增文件 */
400
+ // watcher.on('add', async filePath => {
401
+ // logger.debug(`[热更新][新增插件] ${filePath}`)
402
+ // const name = path.basename(filePath) as fileName
403
+ // if (!name.endsWith('')) return
404
+ // if (!fs.existsSync(`${file}/${name}`)) return
405
+ // /** 载入插件 */
406
+ // const res = await this.createdApp(dir, name, true)
407
+ // if (!res) return
408
+ // /** 延迟1秒 等待卸载完成 */
409
+ // Common.sleep(1000)
410
+ // .then(() => {
411
+ // /** 停止整个文件夹监听 */
412
+ // watcher.close()
413
+ // /** 新增插件之后重新监听文件夹 */
414
+ // this.watcher.delete(dir)
415
+ // this.watchDir(dir)
416
+ // logger.mark(`[新增插件][${dir}][${name}]`)
417
+ // return true
418
+ // })
419
+ // .catch(error => logger.error(error))
420
+ // })
421
+ // /** 监听修改 */
422
+ // watcher.on('change', async PluPath => {
423
+ // const name = path.basename(PluPath) as fileName
424
+ // if (!name.endsWith('')) return
425
+ // if (!fs.existsSync(`${this.dir}/${dir}/${name}`)) return
426
+ // /** 卸载 */
427
+ // this.uninstallApp(dir, name)
428
+ // /** 载入插件 */
429
+ // const res = await this.createdApp(dir, name, true)
430
+ // if (!res) return
431
+ // logger.mark(`[修改插件][${dir}][${name}]`)
432
+ // })
433
+ // /** 监听删除 */
434
+ // watcher.on('unlink', async PluPath => {
435
+ // const name = path.basename(PluPath) as fileName
436
+ // if (!name.endsWith('')) return
437
+ // /** 卸载 */
438
+ // this.uninstallApp(dir, name)
439
+ // /** 停止监听 */
440
+ // watcher.close()
441
+ // /** 重新监听文件夹 */
442
+ // this.watcher.delete(dir)
443
+ // this.watchDir(dir)
444
+ // logger.mark(`[卸载插件][${dir}][${name}]`)
445
+ // })
446
+ // }, 500)
447
+ // /** 生成随机数0.5-2秒 */
448
+ // const random = Math.floor(Math.random() * 1000) + 500
449
+ // Common.sleep(random)
450
+ // .then(() => {
451
+ // const isExist = this.watcher.get(dir)
452
+ // /** 这里需要检查一下是否已经存在,已经存在就关掉之前的监听 */
453
+ // if (isExist) {
454
+ // isExist.close()
455
+ // this.watcher.delete(dir)
456
+ // }
457
+ // this.watcher.set(dir, watcher)
458
+ // return true
459
+ // })
460
+ // .catch(() => {})
461
+ }
462
+ })()
@@ -0,0 +1,26 @@
1
+ import { Express } from 'express';
2
+ import { WebSocketServer } from 'ws';
3
+ import { Server as ServerType, ServerResponse, IncomingMessage } from 'http';
4
+ declare const _default: {
5
+ reg: RegExp;
6
+ list: string[];
7
+ app: Express;
8
+ server: ServerType<typeof IncomingMessage, typeof ServerResponse>;
9
+ WebSocketServer: WebSocketServer;
10
+ RegExp: RegExp;
11
+ /**
12
+ * 监听WebSocket连接并初始化http服务器
13
+ */
14
+ init(): false | any;
15
+ /**
16
+ * HTTP渲染器
17
+ */
18
+ static(): void;
19
+ /**
20
+ * 构建静态资源路径
21
+ */
22
+ staticPath(): void;
23
+ /** 重启当前HTTP服务器 */
24
+ "__#3@#restartServer"(): Promise<void>;
25
+ };
26
+ export default _default;
@@ -0,0 +1,213 @@
1
+ import fs from 'fs'
2
+ import express from 'express'
3
+ import { createServer } from 'http'
4
+ import { WebSocketServer } from 'ws'
5
+ import { render } from '../index.js'
6
+ import connect from '../renderer/wormhole.js'
7
+ import HttpRenderer from '../renderer/http.js'
8
+ import logger from '../utils/logger.js'
9
+ import common from '../utils/common.js'
10
+ import config from '../utils/config.js'
11
+ import listener from './listener.js'
12
+ import exec from '../utils/exec.js'
13
+ export default new (class Server {
14
+ reg
15
+ list
16
+ app
17
+ server
18
+ WebSocketServer
19
+ RegExp
20
+ constructor () {
21
+ this.reg = /(?:)/
22
+ this.list = []
23
+ this.app = express()
24
+ this.server = createServer(this.app)
25
+ this.WebSocketServer = new WebSocketServer({ server: this.server })
26
+ this.RegExp = new RegExp(`(${process.cwd()}|${process.cwd().replace(/\\/g, '/')})`, 'g')
27
+ }
28
+
29
+ /**
30
+ * 监听WebSocket连接并初始化http服务器
31
+ */
32
+ init () {
33
+ try {
34
+ this.WebSocketServer.on('connection', (socket, request) => {
35
+ const path = request.url
36
+ const headers = request.headers
37
+ logger.debug('[反向WS]', path, JSON.stringify(headers, null, 2))
38
+ try {
39
+ const Adapter = listener.getAdapter(path)
40
+ if (!Adapter) {
41
+ logger.error(`[反向WS] 适配器不存在:${path}`)
42
+ return socket.close()
43
+ }
44
+ const KarinAdapter = new Adapter()
45
+ if (typeof KarinAdapter?.server === 'function') {
46
+ KarinAdapter.server(socket, request)
47
+ }
48
+ } catch (error) {
49
+ logger.error(`[反向WS] 注册适配器发生错误:${path}`, error.stack || error.message || error)
50
+ socket.close()
51
+ }
52
+ })
53
+ /** GET接口 - 获取当前启动信息 */
54
+ this.app.get('/api/info', (req, res) => {
55
+ /** 只允许本机ip访问 */
56
+ if (req.hostname === 'localhost' || req.hostname === '127.0.0.1') {
57
+ const data = {
58
+ start: process.env.pm_id ? 'pm2' : 'node',
59
+ start_time: process.uptime().toFixed(2),
60
+ memory: (process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2),
61
+ time: Date.now(),
62
+ }
63
+ res.json(data)
64
+ }
65
+ })
66
+ /** GET接口 - 退出当前进程 */
67
+ this.app.get('/api/exit', async (req) => {
68
+ /** 只允许本机ip访问 */
69
+ if (req.hostname === 'localhost' || req.hostname === '127.0.0.1') {
70
+ logger.mark('[服务器][HTTP] 收到退出请求,即将退出')
71
+ /** 关闭服务器 */
72
+ listener.emit('exit.grpc')
73
+ this.server.close()
74
+ /** 如果是pm2 获取当前pm2ID 使用 */
75
+ if (process.env.pm_id) { await exec(`pm2 delete ${process.env.pm_id}`) }
76
+ /** 正常启动的进程 */
77
+ process.exit()
78
+ }
79
+ })
80
+ /** 监听端口 */
81
+ const { host, port } = config.Server.http
82
+ this.server.listen(port, host, () => {
83
+ logger.mark('[服务器][启动成功][HTTP]: ' + logger.green(`http://${host}:${port}`))
84
+ })
85
+ listener.once('restart.http', () => {
86
+ logger.mark('[服务器][重启][HTTP] 正在重启HTTP服务器...')
87
+ this.#restartServer()
88
+ })
89
+ const { enable, WormholeClient } = config.Server.HttpRender
90
+ if (enable) {
91
+ this.static()
92
+ if (WormholeClient) {
93
+ connect()
94
+ return this
95
+ }
96
+ const { host, post, token } = config.Server.HttpRender
97
+ /** 注册渲染器 */
98
+ const rd = new HttpRenderer(host, post, token)
99
+ render.app({ id: 'puppeteer', type: 'image', render: rd.render.bind(rd) })
100
+ }
101
+ return this
102
+ } catch (error) {
103
+ logger.error('初始化HTTP服务器失败: ', error)
104
+ return false
105
+ }
106
+ }
107
+
108
+ /**
109
+ * HTTP渲染器
110
+ */
111
+ static () {
112
+ this.staticPath()
113
+ /** GET接口 - 渲染 */
114
+ this.app.get('/api/renderHtml', (req, res) => {
115
+ try {
116
+ let { html } = req.query
117
+ if (!html) { return res.status(404).send(JSON.stringify({ code: 404, msg: 'Not Found' })) }
118
+ html = decodeURIComponent(html)
119
+ .replace(/\\/g, '/')
120
+ .replace(/^\.\//, '')
121
+ /** 判断是否为html文件且路径存在 */
122
+ if (!html.endsWith('.html') || !fs.existsSync(html)) {
123
+ const not_found = config.Server.HttpRender.not_found
124
+ if (not_found.startsWith('http')) {
125
+ return res.redirect(not_found)
126
+ } else {
127
+ return res.status(404).send(JSON.stringify({ code: 404, msg: not_found || '?' }))
128
+ }
129
+ }
130
+ let content = fs.readFileSync(html, 'utf-8')
131
+ /** 处理所有绝对路径、相对路径 */
132
+ content = content.replace(this.RegExp, '')
133
+ res.send(content)
134
+ } catch (e) {
135
+ logger.error('[服务器][GET接口 - 渲染]', e)
136
+ res.status(500).send(JSON.stringify({ code: 500, msg: 'Internal Server Error' }))
137
+ }
138
+ })
139
+ /** 拦截静态资源 防止恶意访问 */
140
+ this.app.use((req, res, next) => {
141
+ logger.debug(`[静态资源][${req.headers.host}] ${req.url}`)
142
+ /** 解码 */
143
+ req.url = decodeURIComponent(req.url)
144
+ req.url = req.url
145
+ .replace(/\\/g, '/')
146
+ .replace(/^\.\//, '')
147
+ .replace(/^(\.\.\/)+/, '')
148
+ /** 拦截非资源文件 */
149
+ this.reg.lastIndex = 0
150
+ if (!this.reg.test(req.url)) {
151
+ logger.mark(`[${req.ip}][拦截非资源文件]`, req.url)
152
+ res.status(404).send(JSON.stringify({ code: 404, msg: 'Not Found' }))
153
+ return
154
+ }
155
+ next()
156
+ })
157
+ /** 设置静态文件目录 */
158
+ this.app.use(express.static(process.cwd()))
159
+ }
160
+
161
+ /**
162
+ * 构建静态资源路径
163
+ */
164
+ staticPath () {
165
+ this.list = []
166
+ /** 读取./resources文件夹 */
167
+ const resDir = './resources'
168
+ const resdirs = fs.readdirSync(resDir)
169
+ for (const dir of resdirs) {
170
+ const file = `${resDir}/${dir}`
171
+ if (common.isDir(file)) { this.list.push(file.replace('.', '')) }
172
+ }
173
+ /** 读取./temp/html下所有文件夹 */
174
+ const htmlDir = './temp/html'
175
+ const dirs = fs.readdirSync(htmlDir)
176
+ for (const dir of dirs) {
177
+ const file = `${htmlDir}/${dir}`
178
+ if (common.isDir(file)) { this.list.push(file.replace('.', '')) }
179
+ }
180
+ /** 读取./plugins/html下所有文件夹 */
181
+ const pluginsDir = './plugins'
182
+ const plugins = fs.readdirSync(pluginsDir)
183
+ for (const dir of plugins) {
184
+ const file = `${pluginsDir}/${dir}`
185
+ const resFile = `${file}/resources`
186
+ /** 包含resources文件夹 */
187
+ if (common.isDir(file) && common.isDir(resFile)) { this.list.push(resFile.replace('.', '')) }
188
+ const componentsFile = `${file}/components`
189
+ /** 包含components文件夹 兼容mys */
190
+ if (common.isDir(file) && common.isDir(componentsFile)) { this.list.push(componentsFile.replace('.', '')) }
191
+ }
192
+ this.reg = new RegExp(`(${this.list.join('|')})`, 'g')
193
+ }
194
+
195
+ /** 重启当前HTTP服务器 */
196
+ async #restartServer () {
197
+ try {
198
+ /** 断开所有 WebSocket 连接 */
199
+ for (const ws of this.WebSocketServer.clients) { ws.terminate() }
200
+ /** 关闭当前HTTP服务器 */
201
+ this.server.close()
202
+ /** 延迟1秒 */
203
+ await common.sleep(1000)
204
+ /** 创建一个新的服务器实例 */
205
+ this.server = createServer(this.app)
206
+ this.WebSocketServer = new WebSocketServer({ server: this.server })
207
+ this.init()
208
+ this.static()
209
+ } catch (err) {
210
+ logger.error('[服务器][重启失败]', err)
211
+ }
212
+ }
213
+ })()