master
宇天 2020-09-28 19:20:33 +08:00
commit a1aee3760e
6 changed files with 495 additions and 0 deletions

42
Readme.md Normal file
View File

@ -0,0 +1,42 @@
# Five.js框架控制台工具
> 一键安装、管理Five.js框架的脚本工具。
>> 仅支持Linux/MacOS
## 安装
> 需要全局安装
```bash
npm i five-cli -g
```
## 用法
> 用法: `five-cli [command] args...`
+ Commands:
* init <ver> - 初始化一个版本(最低3.0.0),不指定则为最新版
* start [prod|dev] - 运行当前的应用(可指定以开发环境还是生产环境启动)
* stop - 停止当前应用
* st|status - 查看当前应用状态
* r|restart - 重启当前应用
* del|delete - 删除当前应用
* -h - 查看帮助文档
* -v - 查看工具的版本
## 更新日志
### v0.4.1
* 优化项目构建
* 支持指令简写
* 优化启动逻辑
### v0.2.0
* 从 v0.2版开始, 将只支持 linux/macos, 且框架自动为最新的3.x。
* [优化] 优化five-cli start [dev|prod] 指令
### v0.1.2
* 下载地址改用github
* 兼容windows
* 优化操作提示

100
index.js Normal file
View File

@ -0,0 +1,100 @@
/**
* 脚手架
* @author yutent<yutent.io@gmail.com>
* @date 2020/09/28 11:35:16
*/
const fs = require('iofs')
const chalk = require('chalk')
const path = require('path')
const { do_init } = require('./lib/init')
const { run, stop, restart, remove, status } = require('./lib/pm2')
const { exec } = require('./lib/tools')
var pkg = fs.cat(path.join(__dirname, './package.json'))
var { arch, platform, version } = process
var os = { linux: 'Linux', darwin: 'MacOS', win: 'Windows' }
var cwd = process.cwd()
var args = process.argv.slice(2)
var action = args.shift()
pkg = JSON.parse(pkg)
function print(...args) {
args[0] = args[0].padEnd(20, ' ')
if (args.length > 1) {
args.splice(1, 0, ' - ')
}
console.log.apply(null, args)
}
function logo() {
return chalk.green.bold(` ____
__ _ _ __ ___ | ___|
/ _\` | '_ \` _ \\|___ \\
| (_| | | | | | |___) |
\\__, |_| |_| |_|____/
|___/`)
}
function print_help() {
print('='.repeat(64))
print(`${logo()} v${pkg.version}, 作者: 宇天`)
print('-'.repeat(64))
print('node版本: ' + version)
print('pm2 版本: v' + exec('pm2 -v').trim())
print(`当前系统: ${os[platform]}(${arch})`)
print('当前路径: ' + chalk.red.underline(cwd))
print('='.repeat(64))
print('用法: five-cli [command] args...')
print('Commands:')
print(' init', '初始化框架')
print(' start [prod|dev]', '运行当前的应用(可指定运行环境)')
print(' stop', '停止当前应用')
print(' st|status', '查看当前应用状态')
print(' r|restart', '重启当前应用')
print(' del|delete', '删除当前应用')
print(' -h', '查看帮助文档')
print(' -v', '查看工具的版本')
process.exit()
}
switch (action) {
case 'init':
do_init(cwd)
break
case 'start':
run(cwd, args[0])
break
case 'stop':
stop(cwd)
break
case 'st':
case 'status':
status(cwd)
break
case 'r':
case 'restart':
restart(cwd)
break
case 'del':
case 'delete':
remove(cwd)
break
case '-v':
print(pkg.version)
process.exit()
break
default:
print_help()
// console.log(exec('pm2 -v').trim())
break
}

151
lib/init.js Normal file
View File

@ -0,0 +1,151 @@
/**
* 初始化项目
* @author yutent<yutent.io@gmail.com>
* @date 2020/09/28 15:45:46
*/
const fs = require('iofs')
const path = require('path')
const chalk = require('chalk')
const { exec } = require('./tools')
function encode(obj) {
return JSON.stringify(obj, null, 2)
}
exports.do_init = function(dir) {
var files = fs.ls(dir)
if (files && files.length) {
console.log(chalk.red('当前目录非空, 初始化失败...'))
process.exit()
}
console.log(chalk.green('初始化目录...'))
fs.mkdir(path.join(dir, 'apps'))
fs.mkdir(path.join(dir, 'models'))
fs.mkdir(path.join(dir, 'config'))
fs.mkdir(path.join(dir, 'data'))
fs.mkdir(path.join(dir, 'public'))
fs.mkdir(path.join(dir, 'views'))
fs.echo(
encode({
name: 'fivejs-instance',
type: 'module',
main: 'app.js'
}),
path.join(dir, 'package.json')
)
// git忽略配置
fs.echo(
`
.Spotlight-V100
.Trashes
.DS_Store
.AppleDouble
.LSOverride
._*
.idea
.vscode
node_modules/
data/logs
public/upload/
package-lock.json
app.yaml
`,
path.join(dir, '.gitignore')
)
// 应用控制器
fs.echo(
`
import Controller from '@gm5/controller'
// import Model from '../models/index'
// 所有的应用, 都要继承Controller
// 以获取跟框架交互的能力
export default class Index extends Controller {
// 这个main方法是可选的, 如果有定义, 会自动先调用
// 可以在这里做任何需要预处理的事, 支持async/await
__main__(){
// this.model = new Model(this.ctx.ins('mysql'))
}
async indexAction(){
this.response.end('It works!')
}
}
`,
path.join(dir, 'apps/index.js')
)
// 数据模型
fs.echo(
`
// 数据模型|存储交互
export default class Index {
constructor(conn){
// this.db = conn.emit(false, 'test')
}
list(){
// return this.db.table('user').withFields(['id', 'name']).getAll()
}
}
`,
path.join(dir, 'models/index.js')
)
// 入口js
fs.echo(
`
import Five from '@gm5/core'
const app = new Five()
// 可开启session支持
// app.set({ session: { enabled: true, type: 'redis' } })
// 可开启模板引擎的支持
// app.set({ views: { enabled: true, dir: './views' } })
// 预加载应用, 这一步是必须的, 且需要在listen方法前调用
app.preload('./apps/')
// 中间件示例
// app.use((req, res, next) => {
// if (req.method !== 'GET') {
// res.error('', 401)
// }
// next()
// })
// 安装拓展包, 可以应用中通过 this.context.$$log调用到,
// 在中间件, 或后面的其他拓展包中, 可以直接 this.$$log 调用
// app.install({
// name: 'log',
// install: function() {
// return new Logs('run_time.log', './data/logs/')
// }
// })
app.listen(3000)
`,
path.join(dir, 'app.js')
)
console.log(chalk.green('目录初始化完成, 依赖安装中...'))
exec('npm i @gm5/core')
console.log(chalk.blue('依赖安装完成 ^_^'))
process.exit()
}

170
lib/pm2.js Normal file
View File

@ -0,0 +1,170 @@
/**
* 进程管理
* @author yutent<yutent.io@gmail.com>
* @date 2020/09/28 15:45:46
*/
const fs = require('iofs')
const path = require('path')
const os = require('os')
const chalk = require('chalk')
const { exec, read } = require('./tools')
var cpus = os.cpus()
exports.run = async function(dir, env) {
var confFile = path.join(dir, 'app.yaml')
var run_env = process.env.NODE_ENV || 'development'
var name, cluster, instances
if (env === 'prod') {
run_env = 'production'
}
if (fs.exists(confFile)) {
console.log(
chalk.yellow('应用可能已经启动, 请确认是否在列表中, 以免重复启动!')
)
console.log(exec('pm2 ls'))
var act = await read(
'请确认操作, 如已在列表中, 请回车或按Ctrl + C取消, 输入任意内容将会重新启动: '
)
if (act) {
var data = fs.cat(confFile).toString()
data = data.replace(/NODE_ENV: [a-z]+/, `NODE_ENV: ${run_env}`)
fs.echo(data, confFile)
console.log(exec('pm2 start app.yaml'))
console.log(chalk.blue('应用启动成功!!!'))
}
process.exit()
}
console.log('首次运行,请根据提示完成配置')
// ---------------
name = await read('(请输入应用唯一名称, 不能含中文): ')
if (name === '') {
console.log(chalk.yellow('没有输入, 自动使用随机名称'))
name = 'five-demo-' + ~~(Math.random() * 99)
}
console.log(`当前应用名称为: ${name}`)
// ---------------
cluster = await read('(是否开启集群模式, 生产环境建议开启, y/n): ')
if (cluster === '') {
console.log(chalk.yellow('没有输入, 默认不开启集群模式'))
cluster = 'fork'
} else {
if (cluster === 'y') {
cluster = 'cluster'
} else {
cluster = 'fork'
}
}
console.log(`当前运行模式为: ${cluster}`)
// ---------------
if (cluster === 'cluster') {
instances = await read(`(请设置开启的线程数: 0-${cpus}, 0为自动): `)
instances = +instances
if (instances === 0) {
instances = 'max'
} else {
if (instances > cpus) {
instances = cpus
} else if (instances < 0) {
instances = 1
}
}
instances = `instances: ${instances}`
} else {
instances = ''
}
fs.echo(
`
script: node --experimental-json-modules ./app.js
cwd: ./
watch: true
name: ${name}
ignore_watch: [data, public, package.json, package-lock.json, node_modules, .git, .gitignore, app.yaml]
exec_mode: ${cluster}
${instances}
error_file: ./data/logs/error.log
out_file: ./data/logs/out.log
merge_logs: true
min_uptime: 60s
max_restarts: 1
max_memory_restart: 300M
env:
NODE_ENV: ${run_env}
`,
confFile
)
console.log(chalk.blue('配置完成, 启动中...'))
exec('pm2 start app.yaml')
console.log(chalk.blue('应用成功启动, 打开浏览器体验一下吧.'))
process.exit()
}
exports.stop = async function(dir) {
var confFile = path.join(dir, 'app.yaml')
if (fs.exists(confFile)) {
var confirm = await read('(你确定要停止应用吗? y/n): ')
if (confirm === 'y') {
exec('pm2 stop app.yaml')
console.log(exec('pm2 status app.yaml'))
console.log(chalk.blue('应用已经停止.'))
}
} else {
console.log(chalk.yellow('应用尚未配置启动...'))
}
process.exit()
}
exports.restart = async function(dir) {
var confFile = path.join(dir, 'app.yaml')
if (fs.exists(confFile)) {
var confirm = await read('(你确定要重启应用吗? y/n): ')
if (confirm === 'y') {
console.log(exec('pm2 restart app.yaml'))
console.log(chalk.blue('应用已经重启.'))
}
} else {
console.log(chalk.yellow('应用尚未配置启动...'))
}
process.exit()
}
exports.remove = async function(dir) {
var confFile = path.join(dir, 'app.yaml')
if (fs.exists(confFile)) {
console.log(chalk.yellow('你确定要删除应用吗? '))
console.log(
chalk.yellow('(该操作只是从守护进程列表中移除当前应用,不会删除任何文件)')
)
var confirm = await read('(请确认操作 y/n): ')
if (confirm === 'y') {
exec('pm2 delete app.yaml')
console.log(exec('pm2 status app.yaml'))
console.log(chalk.blue('应用已经删除.'))
}
} else {
console.log(chalk.yellow('应用尚未配置启动...'))
}
process.exit()
}
exports.status = async function(dir) {
var confFile = path.join(dir, 'app.yaml')
if (fs.exists(confFile)) {
console.log(exec('pm2 status app.yaml'))
} else {
console.log(chalk.yellow('应用尚未配置启动...'))
}
process.exit()
}

20
lib/tools.js Normal file
View File

@ -0,0 +1,20 @@
const child = require('child_process')
const readline = require('readline')
exports.exec = function(cmd) {
return child.execSync(cmd).toString()
}
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout
})
exports.read = function(msg) {
return new Promise(r => {
rl.question(msg, _ => {
r(_.trim())
rl.pause()
})
})
}

12
package.json Normal file
View File

@ -0,0 +1,12 @@
{
"name": "@gm5/cli",
"description": "Five.js框架控制台工具",
"version": "0.7.0",
"author": {
"name": "yutent"
},
"bin": {
"five-cli": "index.js"
},
"license": "MIT"
}