q-koa 13.8.58 → 13.8.60
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 +189 -147
- package/package.json +1 -1
package/core/app.js
CHANGED
|
@@ -458,154 +458,88 @@ class APP {
|
|
|
458
458
|
})
|
|
459
459
|
dirList = dirList.filter((item) => includeList.some((i) => i.app === item))
|
|
460
460
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
`${rootDirectory}/${APP_DIR}/${appName}/public`
|
|
474
|
-
),
|
|
475
|
-
router
|
|
476
|
-
)
|
|
477
|
-
|
|
478
|
-
const { push } = require('./file/plugins/log/service')
|
|
461
|
+
const { push } = require('./file/plugins/log/service')
|
|
462
|
+
|
|
463
|
+
// 所有 app 共用同一份 redis 配置,全局只连接一对 client+subscriber 供各 app 复用,
|
|
464
|
+
// 避免每个 app 各自建一套连接(2N 条连接 -> 2 条)
|
|
465
|
+
let sharedRedisClient = null
|
|
466
|
+
let sharedSubscriber = null
|
|
467
|
+
let redisError = null
|
|
468
|
+
if (
|
|
469
|
+
this.config.redis &&
|
|
470
|
+
this.config.redis.host &&
|
|
471
|
+
this.config.redis.port
|
|
472
|
+
) {
|
|
479
473
|
try {
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
await db.authenticate()
|
|
493
|
-
}
|
|
494
|
-
const configPath = `${rootDirectory}/${APP_DIR}/${appName}/config.js`
|
|
495
|
-
const configFile = path.resolve(__dirname, configPath)
|
|
496
|
-
const configExist = fs.existsSync(configFile)
|
|
497
|
-
|
|
498
|
-
if (
|
|
499
|
-
this.config.redis &&
|
|
500
|
-
this.config.redis.host &&
|
|
501
|
-
this.config.redis.port
|
|
502
|
-
) {
|
|
503
|
-
let subscriber
|
|
504
|
-
let client
|
|
505
|
-
try {
|
|
506
|
-
client = redis.createClient({
|
|
507
|
-
socket: {
|
|
508
|
-
host: this.config.redis.host,
|
|
509
|
-
port: this.config.redis.port,
|
|
510
|
-
reconnectStrategy: false,
|
|
511
|
-
},
|
|
512
|
-
...(this.config.redis.database
|
|
513
|
-
? {
|
|
514
|
-
database: this.config.redis.database,
|
|
515
|
-
}
|
|
516
|
-
: {}),
|
|
517
|
-
})
|
|
518
|
-
|
|
519
|
-
client.on('error', (err) => console.log('Redis Client Error', err))
|
|
520
|
-
|
|
521
|
-
await client.connect()
|
|
522
|
-
|
|
523
|
-
subscriber = client.duplicate()
|
|
524
|
-
await subscriber.connect()
|
|
525
|
-
|
|
526
|
-
if (!is_dev) {
|
|
527
|
-
push({
|
|
528
|
-
appName,
|
|
529
|
-
message: `${appName}连接redis成功,${this.config.ip}:${this.port}`,
|
|
530
|
-
})
|
|
531
|
-
}
|
|
532
|
-
} catch (e) {
|
|
533
|
-
client = null
|
|
534
|
-
|
|
535
|
-
push({
|
|
536
|
-
appName,
|
|
537
|
-
message: `${appName}连接redis失败,${e.message}`,
|
|
538
|
-
})
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
this.app[appName] = {
|
|
542
|
-
sequelize: db,
|
|
543
|
-
...(client
|
|
544
|
-
? {
|
|
545
|
-
redisClient: client,
|
|
546
|
-
subscriber,
|
|
547
|
-
}
|
|
548
|
-
: {}),
|
|
549
|
-
appConfig: _.defaultsDeep(
|
|
550
|
-
configExist ? require(path.resolve(__dirname, configPath)) : {},
|
|
551
|
-
this.config.app,
|
|
552
|
-
{ appName, ip: this.config.ip, port: this.port }
|
|
553
|
-
),
|
|
554
|
-
}
|
|
555
|
-
} else {
|
|
556
|
-
this.app[appName] = {
|
|
557
|
-
sequelize: db,
|
|
558
|
-
redisClient: redisVirtual,
|
|
559
|
-
appConfig: _.defaultsDeep(
|
|
560
|
-
configExist ? require(path.resolve(__dirname, configPath)) : {},
|
|
561
|
-
this.config.app,
|
|
562
|
-
{ appName, ip: this.config.ip, port: this.port }
|
|
563
|
-
),
|
|
564
|
-
}
|
|
565
|
-
}
|
|
474
|
+
sharedRedisClient = redis.createClient({
|
|
475
|
+
socket: {
|
|
476
|
+
host: this.config.redis.host,
|
|
477
|
+
port: this.config.redis.port,
|
|
478
|
+
reconnectStrategy: false,
|
|
479
|
+
},
|
|
480
|
+
...(this.config.redis.database
|
|
481
|
+
? {
|
|
482
|
+
database: this.config.redis.database,
|
|
483
|
+
}
|
|
484
|
+
: {}),
|
|
485
|
+
})
|
|
566
486
|
|
|
567
|
-
|
|
487
|
+
sharedRedisClient.on('error', (err) =>
|
|
488
|
+
console.log('Redis Client Error', err)
|
|
489
|
+
)
|
|
568
490
|
|
|
569
|
-
|
|
570
|
-
// this.app.use(proxy(`/${appName}`, proxyConfig));
|
|
571
|
-
// }
|
|
491
|
+
await sharedRedisClient.connect()
|
|
572
492
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
await this.initPlugin(appName)
|
|
493
|
+
sharedSubscriber = sharedRedisClient.duplicate()
|
|
494
|
+
await sharedSubscriber.connect()
|
|
495
|
+
} catch (e) {
|
|
496
|
+
sharedRedisClient = null
|
|
497
|
+
sharedSubscriber = null
|
|
498
|
+
redisError = e
|
|
499
|
+
}
|
|
500
|
+
}
|
|
582
501
|
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
502
|
+
// 数据库连接校验:生产只校验一次(保持原有行为);开发环境按 dbName 去重,同库只校验一次
|
|
503
|
+
let prodAuthPromise = null
|
|
504
|
+
const dbAuthCache = new Map()
|
|
505
|
+
const authenticateDb = (db, dbName, appName) => {
|
|
506
|
+
if (!is_dev) {
|
|
507
|
+
if (!prodAuthPromise) {
|
|
508
|
+
console.log(chalk.green(`${appName}检查数据库${dbName}`))
|
|
509
|
+
prodAuthPromise = db.authenticate()
|
|
586
510
|
}
|
|
511
|
+
return prodAuthPromise
|
|
512
|
+
}
|
|
513
|
+
if (!dbAuthCache.has(dbName)) {
|
|
514
|
+
console.log(chalk.green(`${appName}检查数据库${dbName}`))
|
|
515
|
+
dbAuthCache.set(dbName, db.authenticate())
|
|
516
|
+
}
|
|
517
|
+
return dbAuthCache.get(dbName)
|
|
518
|
+
}
|
|
587
519
|
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
this.
|
|
594
|
-
|
|
595
|
-
|
|
520
|
+
const routers = new Map()
|
|
521
|
+
|
|
522
|
+
// 各 app 的重初始化(数据库校验/插件/模型等)互相独立,并行执行
|
|
523
|
+
const results = await Promise.allSettled(
|
|
524
|
+
dirList.map((appName) =>
|
|
525
|
+
this._initSingleApp(appName, includeList, {
|
|
526
|
+
push,
|
|
527
|
+
routers,
|
|
528
|
+
sharedRedisClient,
|
|
529
|
+
sharedSubscriber,
|
|
530
|
+
redisError,
|
|
531
|
+
authenticateDb,
|
|
596
532
|
})
|
|
533
|
+
)
|
|
534
|
+
)
|
|
535
|
+
const failed = results.find((r) => r.status === 'rejected')
|
|
536
|
+
if (failed) {
|
|
537
|
+
throw failed.reason
|
|
538
|
+
}
|
|
597
539
|
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
push({
|
|
602
|
-
appName,
|
|
603
|
-
message: `${appName}数据库连接失败`,
|
|
604
|
-
})
|
|
605
|
-
console.log(`${appName}数据库连接失败`)
|
|
606
|
-
console.log(e)
|
|
607
|
-
throw new Error(e.message)
|
|
608
|
-
}
|
|
540
|
+
// 按原有顺序挂载路由,保证中间件注册顺序确定
|
|
541
|
+
for (const appName of dirList) {
|
|
542
|
+
const router = routers.get(appName)
|
|
609
543
|
if (
|
|
610
544
|
this.app[appName] &&
|
|
611
545
|
this.app[appName].appConfig &&
|
|
@@ -654,10 +588,122 @@ class APP {
|
|
|
654
588
|
starter.emit('loadAll')
|
|
655
589
|
}
|
|
656
590
|
|
|
657
|
-
|
|
591
|
+
async _initSingleApp(appName, includeList, ctx) {
|
|
592
|
+
const {
|
|
593
|
+
push,
|
|
594
|
+
routers,
|
|
595
|
+
sharedRedisClient,
|
|
596
|
+
sharedSubscriber,
|
|
597
|
+
redisError,
|
|
598
|
+
authenticateDb,
|
|
599
|
+
} = ctx
|
|
600
|
+
console.log(
|
|
601
|
+
chalk.green(`==================== 启动 ${appName} ====================`)
|
|
602
|
+
)
|
|
603
|
+
const routerConfig = {
|
|
604
|
+
prefix: `/${appName}`,
|
|
605
|
+
}
|
|
606
|
+
const router = new Router(routerConfig)
|
|
607
|
+
Serve(
|
|
608
|
+
path.resolve(__dirname, `${rootDirectory}/${APP_DIR}/${appName}/public`),
|
|
609
|
+
router
|
|
610
|
+
)
|
|
611
|
+
routers.set(appName, router)
|
|
612
|
+
|
|
613
|
+
try {
|
|
614
|
+
const target = includeList.find((i) => i.app === appName)
|
|
615
|
+
const dbName = (target ? target.db : '') || this.config.db.name || appName
|
|
616
|
+
const db = connectDatabase(dbName)(this.config.db)
|
|
617
|
+
await authenticateDb(db, dbName, appName)
|
|
618
|
+
|
|
619
|
+
const configPath = `${rootDirectory}/${APP_DIR}/${appName}/config.js`
|
|
620
|
+
const configFile = path.resolve(__dirname, configPath)
|
|
621
|
+
const configExist = fs.existsSync(configFile)
|
|
622
|
+
const appConfig = _.defaultsDeep(
|
|
623
|
+
configExist ? require(configFile) : {},
|
|
624
|
+
this.config.app,
|
|
625
|
+
{ appName, ip: this.config.ip, port: this.port }
|
|
626
|
+
)
|
|
627
|
+
|
|
628
|
+
if (sharedRedisClient) {
|
|
629
|
+
if (!is_dev) {
|
|
630
|
+
push({
|
|
631
|
+
appName,
|
|
632
|
+
message: `${appName}连接redis成功,${this.config.ip}:${this.port}`,
|
|
633
|
+
})
|
|
634
|
+
}
|
|
635
|
+
this.app[appName] = {
|
|
636
|
+
sequelize: db,
|
|
637
|
+
redisClient: sharedRedisClient,
|
|
638
|
+
subscriber: sharedSubscriber,
|
|
639
|
+
appConfig,
|
|
640
|
+
}
|
|
641
|
+
} else if (
|
|
642
|
+
this.config.redis &&
|
|
643
|
+
this.config.redis.host &&
|
|
644
|
+
this.config.redis.port
|
|
645
|
+
) {
|
|
646
|
+
// redis 配置了但连接失败:保持原有行为(不挂 redisClient),仅记录日志
|
|
647
|
+
push({
|
|
648
|
+
appName,
|
|
649
|
+
message: `${appName}连接redis失败,${
|
|
650
|
+
redisError ? redisError.message : '未知错误'
|
|
651
|
+
}`,
|
|
652
|
+
})
|
|
653
|
+
this.app[appName] = {
|
|
654
|
+
sequelize: db,
|
|
655
|
+
appConfig,
|
|
656
|
+
}
|
|
657
|
+
} else {
|
|
658
|
+
this.app[appName] = {
|
|
659
|
+
sequelize: db,
|
|
660
|
+
redisClient: redisVirtual,
|
|
661
|
+
appConfig,
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
const initFileConfig = _.get(
|
|
666
|
+
this.app[appName],
|
|
667
|
+
'appConfig.initFile',
|
|
668
|
+
false
|
|
669
|
+
)
|
|
670
|
+
if (initFileConfig) {
|
|
671
|
+
await this.initFile(appName)
|
|
672
|
+
}
|
|
673
|
+
await this.initPlugin(appName)
|
|
674
|
+
|
|
675
|
+
const initEvent = _.get(this.app[appName], 'appConfig.initEvent', false)
|
|
676
|
+
if (initEvent) {
|
|
677
|
+
this.app[appName].event = new EventEmitter()
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
const cacheConfig = _.get(this.app[appName], 'appConfig.cache', {
|
|
681
|
+
max: 100,
|
|
682
|
+
maxAge: 1000 * 60 * 60 * 24,
|
|
683
|
+
})
|
|
684
|
+
|
|
685
|
+
this.app[appName].cache = new LRUCache({
|
|
686
|
+
max: cacheConfig.max,
|
|
687
|
+
// 兼容业务方两种写法:lru-cache v11 的 ttl 或旧版 maxAge
|
|
688
|
+
ttl: cacheConfig.ttl || cacheConfig.maxAge,
|
|
689
|
+
})
|
|
690
|
+
|
|
691
|
+
await this.initModel(appName)
|
|
692
|
+
} catch (e) {
|
|
693
|
+
push({
|
|
694
|
+
appName,
|
|
695
|
+
message: `${appName}数据库连接失败`,
|
|
696
|
+
})
|
|
697
|
+
console.log(`${appName}数据库连接失败`)
|
|
698
|
+
console.log(e)
|
|
699
|
+
throw new Error(e.message)
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
async initFile(appName) {
|
|
658
704
|
const dirExist = fsExtra.existsSync(path.resolve(__dirname, './file'))
|
|
659
705
|
if (dirExist) {
|
|
660
|
-
fsExtra.
|
|
706
|
+
await fsExtra.copy(
|
|
661
707
|
path.resolve(__dirname, './file'),
|
|
662
708
|
path.resolve(__dirname, `${rootDirectory}/${APP_DIR}/${appName}`),
|
|
663
709
|
{
|
|
@@ -964,12 +1010,8 @@ class APP {
|
|
|
964
1010
|
a[b] = []
|
|
965
1011
|
return a
|
|
966
1012
|
}, {})
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
include,
|
|
970
|
-
},
|
|
971
|
-
this.app[appName]
|
|
972
|
-
)
|
|
1013
|
+
// 直接赋值,避免对整个 app 对象做 defaultsDeep 深合并(且保持对象引用不变)
|
|
1014
|
+
this.app[appName].include = include
|
|
973
1015
|
for (const model of modelList) {
|
|
974
1016
|
|
|
975
1017
|
// if (!this.app[appName].include || !this.app[appName].include[model]) {
|