@@ -1,237 +0,0 @@
import { program , Command } from '@/program.ts' ;
import { checkFileExists , envisionPath , getConfig , writeConfig } from '@/module/index.ts' ;
import path from 'path' ;
import fs from 'fs' ;
import { chalk } from '@/module/chalk.ts' ;
import inquirer from 'inquirer' ;
import { spawn , spawnSync } from 'child_process' ;
import { checkPm2 , installDep , installDeps } from '@/uitls/npm.ts' ;
const command = new Command ( 'init' ) . description ( '初始化应用' ) . action ( ( optison ) = > {
console . log ( 'init' ) ;
} ) ; //
program . addCommand ( command ) ;
const setMainAppConfig = async ( mainAppPath : string ) = > {
const config = getConfig ( ) ;
config . mainAppPath = mainAppPath ;
writeConfig ( config ) ;
} ;
const initMain = async ( ) = > {
const mainAppPath = path . join ( envisionPath , 'main-app' ) ;
const pkgPath = path . join ( mainAppPath , 'package.json' ) ;
if ( ! checkFileExists ( pkgPath ) ) {
fs . mkdirSync ( mainAppPath , { recursive : true } ) ;
const pkg = {
name : 'main-app' ,
version : '1.0.0' ,
type : 'module' ,
main : 'dist/app.mjs' ,
scripts : {
start : 'node dist/app.mjs' ,
pm2 : 'pm2 start dist/app.mjs --name main-app' ,
} ,
dependencies : {
'@kevisual/router' : 'latest' ,
'@kevisual/local-app-manager' : 'latest' ,
'@kevisual/use-config' : 'latest' ,
} ,
} ;
fs . writeFileSync ( pkgPath , JSON . stringify ( pkg , null , 2 ) ) ;
console . log ( chalk . green ( '初始化 main-app 成功' ) ) ;
}
// 在对应的路径,安装依赖
installDeps ( { appPath : mainAppPath , sync : true } ) ;
// 创建 dist/app.mjs
const code = ` import { App } from '@kevisual/router';
import { app as LocalApp } from '@kevisual/local-app-manager';
import { useConfig } from '@kevisual/use-config';
const config = useConfig();
const host = config.host || '127.0.0.1';
LocalApp.listen(config.port, host, () => {
console.log( \` LocalApp is running on http:// \ ${ host } : \ ${ config . port } \` );
});
` ;
const codeDistPath = path . join ( mainAppPath , 'dist' ) ;
if ( ! checkFileExists ( codeDistPath ) ) {
fs . mkdirSync ( codeDistPath , { recursive : true } ) ;
}
fs . writeFileSync ( path . join ( codeDistPath , 'app.mjs' ) , code ) ;
console . log ( chalk . green ( '创建 app.mjs 成功' ) ) ;
// 创建 app.config.json5
const answers = await inquirer . prompt ( [
{
type : 'input' ,
name : 'port' ,
message : '请输入端口号' ,
default : '11015' ,
} ,
{
type : 'input' ,
name : 'host' ,
message : '请输入host' ,
default : '127.0.0.1' ,
} ,
{
type : 'input' ,
name : 'appsPath' ,
message : '请输入apps路径' ,
default : path . join ( mainAppPath , 'apps' ) ,
} ,
] ) ;
const json5 = {
port : Number ( answers . port ) ,
host : answers.host ,
appsPath : answers.appsPath ,
} ;
fs . writeFileSync ( path . join ( mainAppPath , 'app.config.json5' ) , JSON . stringify ( json5 , null , 2 ) ) ;
//
console . log ( chalk . green ( '创建 app.config.json5 成功' ) ) ;
setMainAppConfig ( mainAppPath ) ;
writeConfig ( { . . . getConfig ( ) , appsPath : answers.appsPath } ) ;
} ;
const checkPid = ( pid : number ) = > {
try {
process . kill ( pid , 0 ) ;
return true ;
} catch ( err ) {
if ( err . code === 'ESRCH' ) {
// 进程不存在
console . log ( ` 进程 ${ pid } 不存在 ` ) ;
return false ;
} else if ( err . code === 'EPERM' ) {
// 没有权限访问该进程
console . log ( ` 没有权限检查进程 ${ pid } ` ) ;
return true ;
} else {
// 其他错误
console . error ( ` 检查进程 ${ pid } 时出错: ` , err ) ;
return false ;
}
}
} ;
const mainApp = new Command ( 'main' )
. description ( '初始化main的应用' ) //
. option ( '-i, --init' , '初始化main应用' )
. option ( '-s, --start' , '启动main应用' )
. option ( '-r, --restart' , '重启main应用' )
. option ( '-e, --exit' , '停止main应用' )
. option ( '-p, --pm2' , '使用pm2管理, 只作为启动' )
. action ( async ( options ) = > {
if ( options . init ) {
await initMain ( ) ;
return ;
} else if ( ! options . start && ! options . restart && ! options . exit && ! options . pm2 ) {
console . warn ( chalk . yellow ( '请使用 --init 初始化 main 应用' ) ) ;
}
const runChild = ( ) = > {
const logDir = path . join ( envisionPath , 'log' ) ;
const logFile = path . join ( logDir , 'child.log' ) ;
// 确保日志目录存在
fs . mkdirSync ( logDir , { recursive : true } ) ;
// 如果日志文件不存在,创建一个
if ( ! checkFileExists ( logFile ) ) {
fs . writeFileSync ( logFile , '' ) ;
}
// 如果日志文件大于 10M, 重命名
const stats = fs . statSync ( logFile ) ;
if ( stats . size > 1024 * 1024 * 10 ) {
fs . renameSync ( logFile , path . join ( logDir , ` child- ${ Date . now ( ) } .log ` ) ) ;
fs . writeFileSync ( logFile , '' ) ;
}
// 打开日志文件(追加模式)
const logStream = fs . openSync ( logFile , 'a' ) ;
if ( options . pm2 ) {
if ( ! checkPm2 ( ) ) {
console . log ( '安装pm2' ) ;
installDep ( { isGlobal : true , sync : true , dep : 'pm2' } ) ;
console . log ( chalk . green ( '安装pm2成功' ) ) ;
}
}
let comm = options . pm2 ? 'pm2' : 'node' ;
const args = options . pm2 ? [ 'start' , 'dist/app.mjs' , '--name' , 'main-app' ] : [ 'dist/app.mjs' ] ;
const childProcess = spawn ( comm , args , {
cwd : getConfig ( ) . mainAppPath ,
stdio : [ 'ignore' , logStream , logStream ] , // 忽略 stdio, 重定向到文件
detached : true , // 使子进程独立运行
} ) ;
childProcess . unref ( ) ; // 使子进程独立运行
if ( ! options . pm2 ) {
writeConfig ( { . . . getConfig ( ) , mainAppPid : childProcess.pid } ) ;
}
} ;
const config = getConfig ( ) ;
if ( ! config . mainAppPath ) {
console . log ( '请先初始化 main 应用' ) ;
return ;
}
if ( options . start ) {
if ( config . mainAppPid ) {
// 检查是否有进程
if ( checkPid ( config . mainAppPid ) ) {
console . log ( 'main app 已经启动' ) ;
return ;
}
}
runChild ( ) ;
}
if ( options . restart ) {
if ( config . mainAppPid ) {
// 检查是否有进程
if ( checkPid ( config . mainAppPid ) ) {
process . kill ( config . mainAppPid ) ;
}
}
runChild ( ) ;
} else if ( options . exit ) {
if ( config . mainAppPid ) {
// 检查是否有进程
if ( checkPid ( config . mainAppPid ) ) {
process . kill ( config . mainAppPid ) ;
}
writeConfig ( { . . . config , mainAppPid : null } ) ;
console . log ( 'main app 已经停止' ) ;
}
}
} ) ;
const mainPathCommand = new Command ( 'path' ) . action ( ( ) = > {
const config = getConfig ( ) ;
const appPath = path . resolve ( config . mainAppPath ) ;
console . log ( ` cd ${ appPath } ` ) ;
} ) ;
mainApp . addCommand ( mainPathCommand ) ;
const mainLogCommand = new Command ( 'log' )
. option ( '-t, --tail' , '查看日志' )
. option ( '-f, --follow' , '跟踪日志' )
. description ( '查看main的日志' )
. action ( ( options ) = > {
const logDir = path . join ( envisionPath , 'log' ) ;
const logFile = path . join ( logDir , 'child.log' ) ;
if ( ! checkFileExists ( logFile ) ) {
console . log ( '日志文件不存在' ) ;
return ;
}
if ( options . tail ) {
const childProcess = spawn ( 'tail' , [ '-n' , '20' , '-f' , logFile ] ) ;
childProcess . stdout ? . pipe ( process . stdout ) ;
childProcess . stderr ? . pipe ( process . stderr ) ;
return ;
}
if ( options . follow ) {
const childProcess = spawn ( 'tail' , [ '-n' , '50' , '-f' , logFile ] ) ;
childProcess . stdout ? . pipe ( process . stdout ) ;
childProcess . stderr ? . pipe ( process . stderr ) ;
return ;
}
const log = fs . readFileSync ( logFile , 'utf-8' ) ;
console . log ( log ) ;
} ) ;
mainApp . addCommand ( mainLogCommand ) ;
program . addCommand ( mainApp ) ;