import { Application } from 'egg' ; import { customizerMerge , customizerMergeWithType , getNanoTimeStamp } from '../../Utils' ; import { readFileSync , existsSync } from 'fs' ; import { join } from 'path' ; import { PushEvent } from '../../../plugin/event-manager/events' ; import { HostingClientConfigInitedEvent , HostingClientSyncConfigEvent , HostingClientOnConfigFileChangedEvent , } from '../event' ; import { HostingClientBase } from '../HostingClientBase' ; import { HostingConfigBase } from '../HostingConfigBase' ; import { ClientServiceBase } from './ClientServiceBase' ; import { cloneDeep } from 'lodash' ; export class ConfigService < TConfig extends HostingConfigBase , TRawClient > extends ClientServiceBase < TConfig , TRawClient > { private config : any ; private luaScript : any ; private rawData : RawData ; private version : number ; constructor ( app , client ) { super ( app , client , 'configService' ) ; this . config = { } ; this . luaScript = { } ; this . rawData = { config : { file : { } , mysql : { } , remote : { } } , luaScript : { remote : { } } , } ; this . version = 0 ; } public async onStart ( ) { this . client . eventService . subscribeAll ( PushEvent , async ( e ) => { const hostingConfig = this . client . getHostingBase ( ) . getConfig ( ) ; const filePath = hostingConfig . config . remote . filePath ; if ( e . push . commits . some ( c => c . modified . includes ( filePath ) || c . added . includes ( filePath ) ) ) { this . client . eventService . publish ( 'all' , HostingClientConfigInitedEvent , { installationId : this . client . getHostId ( ) , fullName : this . client . getFullName ( ) , rawData : { config : { remote : await this . loadConfigFromRemote ( ) , } , } as any , version : getNanoTimeStamp ( ) , } ) ; } else if ( e . push . commits . some ( c => c . removed . includes ( filePath ) ) ) { this . client . eventService . publish ( 'all' , HostingClientConfigInitedEvent , { installationId : this . client . getHostId ( ) , fullName : this . client . getFullName ( ) , rawData : { config : { remote : { } , } , } as any , version : getNanoTimeStamp ( ) , } ) ; } if ( hostingConfig . component . enableRepoLua ) { const luaFilePath = hostingConfig . config . remote . luaScriptPath ; if ( e . push . commits . some ( c => c . added . find ( f => f . startsWith ( luaFilePath ) ) || c . modified . find ( f => f . startsWith ( luaFilePath ) ) || c . removed . find ( f => f . startsWith ( luaFilePath ) ) ) ) { this . client . eventService . publish ( 'all' , HostingClientConfigInitedEvent , { installationId : this . client . getHostId ( ) , fullName : this . client . getFullName ( ) , rawData : { luaScript : { remote : await this . loadLuaScriptFromRemote ( ) , } , } as any , version : getNanoTimeStamp ( ) , } ) ; } } } ) ; this . client . eventService . subscribeOne ( HostingClientOnConfigFileChangedEvent , async ( fileEvent ) => { this . logger . info ( 'On file config changed' ) ; const e = { installationId : this . client . getHostId ( ) , fullName : this . client . getFullName ( ) , rawData : { config : { file : { } , } , } as any , version : getNanoTimeStamp ( ) , } ; if ( fileEvent . option === 'update' ) { e . rawData . config . file = await this . loadConfigFromFile ( ) ; } this . client . eventService . publish ( 'all' , HostingClientConfigInitedEvent , e ) ; } ) ; this . client . eventService . subscribeAll ( HostingClientConfigInitedEvent , async ( e ) => { this . logger . info ( 'Hosting client config inited, update local config' ) ; if ( this . version < e . version ) { this . version = e . version ; if ( e . rawData . config ) { Object . keys ( e . rawData . config ) . forEach ( key => { this . rawData . config [ key ] = e . rawData . config [ key ] ; } ) ; await this . mergeConfig ( ) ; } if ( e . rawData . luaScript ) { Object . keys ( e . rawData . luaScript ) . forEach ( key => { this . rawData . luaScript [ key ] = e . rawData . luaScript [ key ] ; } ) ; } await this . mergeLuaScript ( ) ; this . client . onConfigLoaded ( ) ; } } ) ; this . client . eventService . subscribeOne ( HostingClientSyncConfigEvent , ( ) => this . syncData ( ) ) ; } public async onDispose ( ) { } public async onConfigLoaded ( ) { } public async syncData ( ) { this . rawData . config . file = await this . loadConfigFromFile ( ) ; this . rawData . config . mysql = await this . loadConfigFromMysql ( ) ; this . rawData . config . remote = await this . loadConfigFromRemote ( ) ; await this . mergeConfig ( ) ; const hostingConfig = this . client . getHostingBase ( ) . getConfig ( ) ; if ( hostingConfig . component . enableRepoLua ) { this . rawData . luaScript . remote = await this . loadLuaScriptFromRemote ( ) ; } this . client . eventService . publish ( 'all' , HostingClientConfigInitedEvent , { installationId : this . client . getHostId ( ) , fullName : this . client . getFullName ( ) , rawData : this . rawData , version : getNanoTimeStamp ( ) , } ) ; } /**************************** config ****************************/ private async mergeConfig ( ) { const mergeConfig = customizerMerge ( this . rawData . config . file , this . rawData . config . mysql , this . rawData . config . remote ) ; const defaultConfig = await this . client . getHostingBase ( ) . compService . getDefaultConfig ( mergeConfig ) ; this . config = customizerMergeWithType ( defaultConfig , mergeConfig ) ; } private async loadConfigFromFile ( ) { try { const hostingConfig = this . client . getHostingConfig ( ) ; if ( hostingConfig . config . private . file ) { const fileFullPath = this . genRepoConfigFilePath ( hostingConfig . config . private . file . rootPath ) ; if ( existsSync ( fileFullPath ) ) return JSON . parse ( readFileSync ( fileFullPath ) . toString ( ) ) ; } } catch ( error ) { this . logger . error ( error ) ; } return { } ; } private async loadConfigFromMysql ( ) { return { } ; } private async loadConfigFromRemote ( ) { try { const hostingConfig = this . client . getHostingConfig ( ) ; const remoteConfig = await this . client . getFileContent ( hostingConfig . config . remote . filePath ) ; if ( remoteConfig ) return JSON . parse ( remoteConfig . content ) ; } catch ( error ) { this . logger . error ( error ) ; } return { } ; } private genRepoConfigFilePath ( rootPath ) { return join ( rootPath , this . client . getFullName ( ) + '.json' ) ; } public getConfig ( ) { return this . config ; } public getCompConfig < TConfig > ( comp ) { if ( this . config [ comp ] ) { return this . config [ comp ] as TConfig ; } return undefined ; } /**************************** luaScript ****************************/ private async mergeLuaScript ( ) { const defaultLuaComps = await this . client . getHostingBase ( ) . compService . getDefaultLuaScript ( this . config ) ; const mergedLuaScript = customizerMergeWithType ( defaultLuaComps , this . rawData . luaScript . remote ) ; if ( mergedLuaScript ) { this . luaScript = { } ; Object . keys ( mergedLuaScript ) . forEach ( compName => { if ( this . config [ compName ] && mergedLuaScript [ compName ] ) { this . luaScript [ compName ] = cloneDeep ( mergedLuaScript [ compName ] ) ; } } ) ; } } private async loadLuaScriptFromRemote ( ) { const repoLuaComps = { } ; try { const hostingConfig = this . client . getHostingConfig ( ) ; if ( ! hostingConfig . component . enableRepoLua ) return { } ; const compNames = Object . keys ( this . config ) ; for ( const i in compNames ) { const luaCompPath = this . genRepoLuaFilePath ( hostingConfig . config . remote . luaScriptPath , compNames [ i ] ) ; const file = await this . client . getFileContent ( luaCompPath ) ; if ( file && file . content ) repoLuaComps [ compNames [ i ] ] = file . content ; } } catch ( error ) { this . logger . error ( error ) ; } return repoLuaComps ; } private genRepoLuaFilePath ( rootPath , componentName ) { return join ( rootPath , componentName + '.lua' ) ; } public getLuaScript ( ) { return this . getLuaScriptAndOffset ( ) . luaScript ; } public getLuaScriptOffset ( ) { return this . getLuaScriptAndOffset ( ) . offset ; } public getLuaScriptAndOffset ( ) { let luaScript = '' ; const luaScriptOffset = [ ] ; Object . keys ( this . luaScript ) . forEach ( compName => { const head = `local ${ compName } = function ()
  local compName = '${ compName }'
  local compConfig = config.${ compName }
` ; const tail = `
end
${ compName }()
` ; const compScript = head + this . luaScript [ compName ] + tail ; luaScript += compScript ; luaScriptOffset . push ( { compName , offset : compScript . split ( '\n' ) . length - 1 } ) ; } ) ; return { luaScript , offset : luaScriptOffset } ; } } export interface RawData { config : { file : any ; mysql : any ; remote : any ; } ; luaScript : { remote : any ; } ; }