q-koa 11.3.4 → 11.4.0

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.
package/core/app.js CHANGED
@@ -853,6 +853,7 @@ class APP {
853
853
  !_.get(app, `${appName}.model.${controller}`) ||
854
854
  !_.get(app, `${appName}.controller.${controller}`)
855
855
  ) {
856
+ console.error(`${controller}`)
856
857
  throw new Error('路由model定义缺失')
857
858
  }
858
859
  await next()
@@ -870,6 +871,7 @@ class APP {
870
871
  !_.get(app, `${appName}.model.${controller}.${fn}`) &&
871
872
  !_.get(app, `${appName}.controller.${controller}.${fn}`)
872
873
  ) {
874
+ console.error(`${controller}.${fn}`)
873
875
  throw new Error('路由fn定义缺失')
874
876
  }
875
877
 
@@ -45,3 +45,599 @@ exports.initModel = async (ctx) => {
45
45
  return ctx.ERROR(`app.model没有${model}`)
46
46
  }
47
47
  }
48
+
49
+ exports.customRouter = async (ctx) => {
50
+ // const {
51
+ // app,
52
+ // appName,
53
+ // } = getAppByCtx(ctx);
54
+
55
+ // const list = [{
56
+ // path: 'jiangong_statis',
57
+ // name: '数据面板'
58
+ // }]
59
+ const list = []
60
+ ctx.SUCCESS(list)
61
+ return list
62
+ }
63
+
64
+ exports.showTables = async (ctx) => {
65
+ const { app, appName } = getAppByCtx(ctx)
66
+
67
+ if (app.cache && app.cache.get('showTables')) {
68
+ ctx.SUCCESS(app.cache.get('showTables'))
69
+ return app.cache.get('showTables')
70
+ }
71
+
72
+ const pluginDir = path.resolve(
73
+ __dirname,
74
+ `${process.cwd()}/app/${appName}/plugins`
75
+ )
76
+ let aliasModelList = []
77
+ const allList = await fsPromise.readdir(pluginDir)
78
+ allList.forEach((folder) => {
79
+ const isFolder = fs.lstatSync(path.resolve(pluginDir, folder)).isDirectory()
80
+ const hasConfig = fs.existsSync(
81
+ path.resolve(
82
+ __dirname,
83
+ `${process.cwd()}/app/${appName}/plugins/${folder}/config.js`
84
+ )
85
+ )
86
+ let hasModel = false
87
+ if (hasConfig) {
88
+ const folderConfig = require(`${process.cwd()}/app/${appName}/plugins/${folder}/config.js`)
89
+ if (folderConfig.model) {
90
+ hasModel = true
91
+ }
92
+ }
93
+
94
+ if (
95
+ isFolder &&
96
+ hasConfig &&
97
+ hasModel &&
98
+ !Object.keys(app.model).includes(folder)
99
+ ) {
100
+ aliasModelList = [...aliasModelList, folder]
101
+ }
102
+ })
103
+ const modelList = [...Object.keys(app.model), ...aliasModelList]
104
+ const models = modelList.map((_model) => {
105
+ const defaultExcludes = [
106
+ 'id',
107
+ 'created_at',
108
+ 'updated_at',
109
+ 'deleted_at',
110
+ 'createdid',
111
+ ]
112
+ const mock = !lodash.isEmpty(
113
+ app.mock && app.mock[_model] && app.mock[_model]()
114
+ )
115
+ const defaultConfigPath = path.resolve(__dirname, `../${_model}/config.js`)
116
+ const defaultConfigExist = fs.existsSync(defaultConfigPath)
117
+ const target =
118
+ app.config[_model] &&
119
+ Object.keys(app.config[_model]).length === 0 &&
120
+ defaultConfigExist
121
+ ? require(defaultConfigPath)
122
+ : app.config[_model]
123
+ // const target = Object.keys(app.config[model])
124
+
125
+ if (!target) throw new Error(`检查是否有${_model}/config.js`)
126
+ const model = target.model ? target.model : _model
127
+ return {
128
+ modelName: _model,
129
+ model,
130
+ name: target ? target.name : '',
131
+ // 属于哪个分类
132
+ belongs: lodash.get(target, 'belongs', 'page'),
133
+ // list切换tab显示 ex lanuage
134
+ limit: lodash.get(target, 'limit', 20),
135
+ excludeAuth: lodash.get(target, 'excludeAuth', []),
136
+ // 可排序字段
137
+ order: lodash.get(target, 'order', []),
138
+ // 默认排序
139
+ defaultOrder: lodash.get(target, 'defaultOrder', []),
140
+ // 下拉筛选
141
+ select: lodash.get(target, 'select', []),
142
+ // 是否可以选择批量删除
143
+ multiple: lodash.get(target, 'multiple', true),
144
+ // 是否后台拆分sql
145
+ is_split: lodash.get(target, 'is_split', false),
146
+ // 是否后台拆分count
147
+ is_split_count: lodash.get(target, 'is_split_count', false),
148
+ // 是否后台显示虚拟字段
149
+ show_virtual: lodash.get(target, 'show_virtual', false),
150
+ // 忽略字段
151
+ excludes:
152
+ _model === 'setting'
153
+ ? lodash.get(target, 'excludes', [])
154
+ : lodash.difference(
155
+ lodash.uniq([
156
+ ...defaultExcludes,
157
+ ...lodash.get(target, 'excludes', []),
158
+ ]),
159
+ lodash.get(target, 'order', [])
160
+ ),
161
+ // reference admin components
162
+ reference: lodash.get(target, 'reference', []),
163
+ // reference admin components
164
+ personal: lodash.get(target, 'personal', []),
165
+ // sortOrder router
166
+ sortOrder: lodash.get(target, 'sortOrder', 1),
167
+
168
+ attributes: lodash.mapValues(
169
+ app.attributes[model],
170
+ (item) => item.comment
171
+ ),
172
+
173
+ // include for admin
174
+ include: lodash.get(target, 'include', ''),
175
+ // only include exsit, and for custom like user_distribute has two user model
176
+ relate: lodash.get(target, 'relate', ''),
177
+ // can be mock boolean
178
+ mock,
179
+ // reference model query
180
+ referenceSelect: lodash.get(target, 'referenceSelect', []),
181
+ // 是否可以选择批量删除
182
+ availableSort: lodash.get(target, 'availableSort', false),
183
+ // 用于后台管理不要查询太多数据,常用于表多的model
184
+ autoData: lodash.get(target, 'autoData', null),
185
+ // 有些model需要前置查询其它model数据
186
+ initList: lodash.get(target, 'initList', []),
187
+ // 有些table需要前置查询数据
188
+ initTableList: lodash.get(target, 'initTableList', []),
189
+ // 局部更新列表
190
+ editInline: lodash.get(target, 'editInline', {}),
191
+ // 有些model需要前置查询其它model数据
192
+ comment: lodash.get(target, 'comment', {}),
193
+ // 默认查询,覆盖之前
194
+ modelQuery: lodash.get(target, 'modelQuery', {}),
195
+ // 默认更新,覆盖之前
196
+ modelUpsert: lodash.get(target, 'modelUpsert', {}),
197
+ // 软删除比如is_on:false,就upsert
198
+ modelDelete: lodash.get(target, 'modelDelete', {}),
199
+ // 删除时检查是不是含有
200
+ deleteCheckList: lodash.get(target, 'deleteCheckList', []),
201
+ // 批量更新字段
202
+ bulkCreateList: lodash.get(target, 'bulkCreateList', []),
203
+
204
+ fn: app.controller[_model] ? Object.keys(app.controller[_model]) : [],
205
+ }
206
+ })
207
+
208
+ ctx.SUCCESS(models)
209
+ app.cache && app.cache.set('showTables', models)
210
+ return models
211
+ }
212
+
213
+ exports.getTable = async (ctx) => {
214
+ const { app } = getAppByCtx(ctx)
215
+
216
+ const {
217
+ model: _model,
218
+ show_virtual = false,
219
+ is_cache = true,
220
+ } = ctx.request.body
221
+ if (_model === 'page') return ctx.SUCCESS({})
222
+ if (is_cache && app.cache && app.cache.get(`${_model}-table`)) {
223
+ return ctx.SUCCESS(app.cache.get(`${_model}-table`))
224
+ }
225
+
226
+ const model = app.config[_model].model || _model
227
+
228
+ const _result = await app.sequelize.getQueryInterface().describeTable(model)
229
+
230
+ const modelAttributes = lodash.cloneDeep(app.attributes[model])
231
+ const result = lodash.pick(_result, [
232
+ ...Object.keys(modelAttributes),
233
+ 'id',
234
+ 'createdid',
235
+ 'updated_at',
236
+ 'created_at',
237
+ ])
238
+
239
+ const obj = {}
240
+ if (show_virtual) {
241
+ Array.from(
242
+ new Set([...Object.keys(result), ...Object.keys(modelAttributes)])
243
+ ).forEach((attr) => {
244
+ obj[attr] = lodash.merge(
245
+ modelAttributes[attr],
246
+ lodash.omitBy(result[attr], lodash.isNull),
247
+ app.config[_model].comment && app.config[_model].comment
248
+ ? app.config[_model].comment[attr]
249
+ : {}
250
+ )
251
+ })
252
+ } else {
253
+ Object.keys(result).forEach((attr) => {
254
+ obj[attr] = lodash.merge(
255
+ modelAttributes[attr],
256
+ lodash.omitBy(result[attr], lodash.isNull),
257
+ app.config[_model].comment && app.config[_model].comment
258
+ ? app.config[_model].comment[attr]
259
+ : {}
260
+ )
261
+ })
262
+ }
263
+
264
+ app.cache && app.cache.set(`${_model}-table`, obj)
265
+ return ctx.SUCCESS(obj)
266
+ }
267
+
268
+ exports.dropModel = async (ctx) => {
269
+ const { app } = getAppByCtx(ctx)
270
+
271
+ const { model } = ctx.request.body
272
+ const result = await app.model[model].drop()
273
+ return ctx.SUCCESS(result)
274
+ }
275
+
276
+ exports.getImage = async (ctx) => {
277
+ const { app } = getAppByCtx(ctx)
278
+
279
+ const appConfig = getConfig(app)
280
+
281
+ const { site_host } = await appConfig.getObject('base')
282
+ // const { dir, upload } = app.config.static;
283
+ const targetDir = `${path.resolve(
284
+ __dirname,
285
+ `${process.cwd()}/public/upload`
286
+ )}`
287
+ const files = await fsPromise.readdir(targetDir)
288
+ const images = files.filter((file) =>
289
+ /\w(\.gif|\.jpeg|\.png|\.jpg|\.bmp)/i.test(file)
290
+ )
291
+ const result = []
292
+ for (let i = 0; i < images.length; i++) {
293
+ const image = `${path.resolve(
294
+ __dirname,
295
+ `${process.cwd()}/public/upload/${images[i]}`
296
+ )}`
297
+
298
+ result.push({
299
+ img: `http://${site_host || 'api.kuashou.com'}/upload/${images[i]}`,
300
+ name: images[i],
301
+ ...fs.statSync(image),
302
+ })
303
+ }
304
+
305
+ ctx.SUCCESS(result)
306
+ }
307
+
308
+ exports.mock = async (ctx) => {
309
+ const { app } = getAppByCtx(ctx)
310
+
311
+ const { model } = ctx.request.body
312
+ const result = app.mock && app.mock[model] && app.mock[model]()
313
+ return ctx.SUCCESS(!lodash.isEmpty(result))
314
+ }
315
+
316
+ exports.table = async (ctx) => {
317
+ const { app } = getAppByCtx(ctx)
318
+ const { model } = ctx.request.body
319
+ if (!model) {
320
+ const result = Object.keys(app.model).map((m) => ({
321
+ [m]: lodash.mapValues(app.attributes[m], (item) => item.comment),
322
+ }))
323
+ ctx.SUCCESS(result)
324
+ } else {
325
+ const result = app.attributes[model]
326
+ ctx.SUCCESS(result)
327
+ }
328
+ }
329
+
330
+ exports.showAllSchemas = async (ctx) => {
331
+ const { app } = getAppByCtx(ctx)
332
+
333
+ const { model } = ctx.request.body
334
+ const result = await app.sequelize.getQueryInterface().describeTable(model)
335
+ return ctx.SUCCESS(result)
336
+ }
337
+
338
+ exports.initDataWithCache = exports.initData = async (ctx) => {
339
+ const { app } = getAppByCtx(ctx)
340
+ const appConfig = getConfig(app)
341
+
342
+ const { is_cache } = await appConfig.getObject('base')
343
+ const { includes, excludes = [] } = ctx.request.body
344
+
345
+ const cacheTarget = JSON.stringify(ctx.request.body)
346
+
347
+ let result = null
348
+ if (is_cache && cache.get(cacheTarget)) {
349
+ result = cache.get(cacheTarget)
350
+ } else {
351
+ result = await app.service.system.initData({
352
+ app,
353
+ includes,
354
+ excludes,
355
+ ctx,
356
+ })
357
+ if (is_cache && cacheTarget.includes('setting/findAll')) {
358
+ cache.set(cacheTarget, result)
359
+ }
360
+ }
361
+ return ctx.SUCCESS(result)
362
+ }
363
+
364
+ exports.recentlyError = async (ctx) => {
365
+ // const {
366
+ // app,
367
+ // appName,
368
+ // } = getAppByCtx(ctx);
369
+
370
+ if (ctx.ws) {
371
+ const ws = await ctx.ws()
372
+ const send = (data) => ws.send(JSON.stringify(data))
373
+ ctx.app.on('error', async (err) => {
374
+ console.log(err)
375
+ try {
376
+ send({
377
+ type: 'error',
378
+ payload: err ? err.errorData : {},
379
+ })
380
+ } catch (e) {
381
+ console.log(e)
382
+ }
383
+ })
384
+ } else {
385
+ throw new Error('这是一个websocket url')
386
+ }
387
+ }
388
+
389
+ exports.sandbox = async (ctx) => {
390
+ const code = `
391
+ module.exports = async (callback)=>{
392
+ callback({
393
+ name:1
394
+ })
395
+ }
396
+ `
397
+ // const { code } = ctx.request.body;
398
+ const vm = new VM({
399
+ sandbox: {
400
+ axios,
401
+ cheerio,
402
+ module,
403
+ setTimeout,
404
+ sleep: (time) =>
405
+ new Promise((resolve) => setTimeout(resolve, time * 1000)),
406
+ },
407
+ })
408
+ vm.run(code)(ctx.SUCCESS)
409
+ }
410
+
411
+ exports.onlineChat = async (ctx) => {
412
+ if (ctx.ws) {
413
+ const ws = await ctx.ws()
414
+ const send = (data) => ws.send(JSON.stringify(data))
415
+ ws.on('message', async (code) => {
416
+ const vm = new VM({
417
+ sandbox: {
418
+ send: (data) => ws.send(JSON.stringify(data)),
419
+ axios,
420
+ cheerio,
421
+ },
422
+ })
423
+ vm.run(code)
424
+ const unhandledRejections = {}
425
+
426
+ process.on('unhandledRejection', (reason) => {
427
+ if (unhandledRejections[reason]) {
428
+ console.log('====?', reason)
429
+ } else {
430
+ send({
431
+ type: 'error',
432
+ payload: `${reason}`,
433
+ })
434
+ unhandledRejections[reason] = true
435
+ }
436
+ })
437
+ })
438
+ } else {
439
+ throw new Error('这是一个websocket url')
440
+ }
441
+ }
442
+
443
+ exports.chat = async (ctx) => {
444
+ const { app } = getAppByCtx(ctx)
445
+ if (ctx.ws) {
446
+ const ws = await ctx.ws()
447
+ const send = (data) => ws.send(JSON.stringify(data))
448
+ ws.on('message', async (payload) => {
449
+ const message = JSON.parse(payload)
450
+ const result = await app.model.ask.create({
451
+ user_id: message.payload.user_id,
452
+ content: message.payload.content,
453
+ is_ask: Boolean(message.payload.user_id),
454
+ })
455
+
456
+ send({
457
+ type: 'message',
458
+ payload: result,
459
+ })
460
+ })
461
+ } else {
462
+ throw new Error('这是一个websocket url')
463
+ }
464
+ }
465
+
466
+ exports.task = async (ctx) => {
467
+ const { app } = getAppByCtx(ctx)
468
+
469
+ const { task } = ctx.request.body
470
+
471
+ if (app.task && app.task[task]) {
472
+ await app.task[task](app)
473
+ }
474
+ ctx.SUCCESS(task)
475
+ }
476
+
477
+ exports.dashboard = async (ctx) => {
478
+ // const {
479
+ // app,
480
+ // } = getAppByCtx(ctx);
481
+
482
+ const result = [
483
+ // {
484
+ // name: '用户',
485
+ // number: 5,
486
+ // route: 'page/user'
487
+ // }
488
+ ]
489
+ ctx.SUCCESS(result)
490
+ }
491
+
492
+ exports.showAllModel = async (ctx) => {
493
+ const { app, appName } = getAppByCtx(ctx)
494
+ const { is_count = false } = ctx.request.body
495
+ const result = await app.sequelize.showAllSchemas()
496
+ const list = result.map(
497
+ (item) => item[`Tables_in_${app.sequelize.config.database}`]
498
+ )
499
+ let tableList = []
500
+ for (let i = 0; i < list.length; i++) {
501
+ if (!app.model[list[i]]) continue
502
+ const defaultConfigPath = path.resolve(__dirname, `../${list[i]}/config.js`)
503
+ const defaultConfigExist = fs.existsSync(defaultConfigPath)
504
+ const target =
505
+ app.config[list[i]] &&
506
+ Object.keys(app.config[list[i]]).length === 0 &&
507
+ defaultConfigExist
508
+ ? require(defaultConfigPath)
509
+ : app.config[list[i]]
510
+ let count = 0
511
+ if (is_count) {
512
+ count = await app.model[list[i]].count()
513
+ }
514
+ tableList = [
515
+ ...tableList,
516
+ {
517
+ count,
518
+ name: target.name,
519
+ model: list[i],
520
+ count,
521
+ },
522
+ ]
523
+ }
524
+ return ctx.SUCCESS(
525
+ tableList.map((item) => lodash.omit(item, is_count ? [] : ['count']))
526
+ )
527
+ }
528
+
529
+ exports.drop = async (ctx) => {
530
+ const { app, appName } = getAppByCtx(ctx)
531
+
532
+ const result = await app.sequelize.showAllSchemas()
533
+ const list = result.map(
534
+ (item) => item[`Tables_in_${app.sequelize.config.database}`]
535
+ )
536
+ const filterList = list.filter((item) => !app.model[item])
537
+ await Promise.all(
538
+ filterList.map((folder) => {
539
+ const model = app.sequelize.define(
540
+ folder,
541
+ {
542
+ id: {
543
+ type: Sequelize.INTEGER(11),
544
+ allowNull: false,
545
+ primaryKey: true,
546
+ autoIncrement: true,
547
+ },
548
+ },
549
+ {
550
+ freezeTableName: true,
551
+ comment: folder,
552
+ createdAt: 'created_at',
553
+ updatedAt: 'updated_at',
554
+ name: {
555
+ singular: folder,
556
+ plural: `${folder}s`,
557
+ },
558
+ }
559
+ )
560
+ return model.drop()
561
+ })
562
+ )
563
+
564
+ ctx.SUCCESS(`删除${filterList.length}个无用表`)
565
+ }
566
+
567
+ exports.mockPay = async (ctx) => {
568
+ const { app } = getAppByCtx(ctx)
569
+ const { order_id, order_price, prefix, ...rest } = ctx.request.body
570
+ const model = prefix ? `${prefix}_order` : 'order'
571
+ console.log(model, 'model')
572
+ if (!(app.service[model] && app.service[model].notify)) {
573
+ throw new Error(`没配置${model}支付回调notify`)
574
+ }
575
+ await app.service[model].notify({
576
+ app,
577
+ order_id,
578
+ order: model,
579
+ order_price,
580
+ ...rest,
581
+ })
582
+ ctx.SUCCESS('ok')
583
+ }
584
+
585
+ exports.copyOther = async (ctx) => {
586
+ const { app, appName } = getAppByCtx(ctx)
587
+
588
+ const { model, source } = ctx.request.body
589
+ if (!model) return ctx.ERROR('model?')
590
+ if (!source) return ctx.ERROR('source? https://www.kuashou.com/kuashou')
591
+ let list = []
592
+ if (Array.isArray(model)) {
593
+ list = model
594
+ } else {
595
+ if (model !== 'all') {
596
+ list = [model]
597
+ } else {
598
+ const result = await app.sequelize.showAllSchemas()
599
+ list = result.map(
600
+ (item) => item[`Tables_in_${app.sequelize.config.database}`]
601
+ )
602
+ }
603
+ }
604
+
605
+ const prefix = `${source}`
606
+ let successList = []
607
+ let failList = []
608
+ for (let i = 0; i < list.length; i++) {
609
+ const url = `${prefix}/${list[i]}/findAll`
610
+
611
+ const result = await axios
612
+ .post(
613
+ url,
614
+ {},
615
+ {
616
+ headers: {
617
+ 'Client-Type': 0,
618
+ },
619
+ }
620
+ )
621
+ .then((res) => res.data)
622
+ if (result.code === 200) {
623
+ await app.model[list[i]].sync({
624
+ force: true,
625
+ })
626
+ try {
627
+ await app.model[list[i]].bulkCreate(
628
+ result.data.map((i) => lodash.omitBy(i, lodash.isNull)),
629
+ {}
630
+ )
631
+ successList = [...successList, list[i]]
632
+ } catch (e) {
633
+ console.log(e.message)
634
+ failList = [...failList, list[i]]
635
+ }
636
+ } else {
637
+ console.log(url, result.data)
638
+ failList = [...failList, list[i]]
639
+ continue
640
+ }
641
+ }
642
+ ctx.SUCCESS({ successList, failList })
643
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "q-koa",
3
- "version": "11.3.4",
3
+ "version": "11.4.0",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {