const express = require('express'); const router = express.Router(); const fs = require('fs'); const path = require('path'); const process = require('child_process'); const projectsFile = path.join(__dirname, 'project') const getDatetime = (fmt = "YYYY-mm-dd HH:MM", date) => { date = date || new Date(); let ret; const opt = { "Y+": date.getFullYear().toString(), // 年 "m+": (date.getMonth() + 1).toString(), // 月 "d+": date.getDate().toString(), // 日 "H+": date.getHours().toString(), // 时 "M+": date.getMinutes().toString(), // 分 "S+": date.getSeconds().toString() // 秒 // 有其他格式化字符需求可以继续添加,必须转化成字符串 }; for (let k in opt) { ret = new RegExp("(" + k + ")").exec(fmt); if (ret) { fmt = fmt.replace(ret[1], (ret[1].length == 1) ? (opt[k]) : (opt[k].padStart(ret[1].length, "0"))) } } ; return fmt; } router.get('/', function (req, res, next) { const data = fs.readFileSync(projectsFile + '/index.json'); res.json(JSON.parse(data.toString())) }); router.get('/history', (req, res, next) => { let {id, time} = req.query; const fileName = projectsFile + '/' + id + '/' + time + '.log'; if (!fs.existsSync(fileName)) { res.json({code: 1, message: "日志不存在", fileName}); return; } res.sendFile(fileName, {}, function (err) { if (err) { next(err); } }); }); // 读取编译记录 router.get('/:id', function (req, res, next) { const id = req.params.id; const file = projectsFile + '/' + id + '/index.json'; if (!fs.existsSync(file)) { res.json({code: 1, message: "项目不存在"}); return; } const data = fs.readFileSync(file); res.json(JSON.parse(data.toString())) }); router.get('/shell/:id', (req, res) => { const path = projectsFile + '/' + req.params.id + '/build.sh'; if (!fs.existsSync(path)) { res.send({code: 1, message: '脚本文件不存在'}) return; } const shell = fs.readFileSync(path,{encoding:'utf-8'}) res.send({code: 0, shell}) }); router.post('/shell/:id', (req, res) => { const path = projectsFile + '/' + req.params.id + '/build.sh'; if (!fs.existsSync(path)) { res.send({code: 1, message: '脚本文件文件不存在'}) return; } const shell = req.body.shell; fs.writeFileSync(path, shell,{encoding:"utf-8"}) res.send({code: 0}) }); const writeExecLog = (id, status, running = false) => { const data = JSON.parse(fs.readFileSync(projectsFile + '/index.json')); data[id - 1].lastUpdate = getDatetime('YYYY-mm-dd HH:MM:SS'); data[id - 1].lastStatus = status; data[id - 1].running = running; fs.writeFile(projectsFile + '/index.json', JSON.stringify(data), (err1) => { if (err1) { console.log('writeExecLog:write project file fail') } }) } // 执行 router.all('/hook/:id', function (req, res, next) { const id = parseInt(req.params.id); const projects = JSON.parse(fs.readFileSync(projectsFile + '/index.json',{encoding:'utf-8'})); if (id < 1 || id > projects.length) { res.json({code: 1, message: "项目不存在(1)"}); return; } if (projects[id - 1].running) { res.json({code: 3, message: "当前项目脚本正在运行中"}); return; } const file = projectsFile + '/' + id + '/build.sh'; if (!fs.existsSync(file)) { res.json({code: 1, message: "项目不存在(2)"}); return; } res.json({code: 0, message: "开始执行脚本,请稍后查看运行结果"}); // 历史记录 fs.readFile(projectsFile + '/' + id + '/index.json', (err, data) => { if (err) { console.log('read project {', id, '} history failed', err) return; } const histories = JSON.parse(data.toString()); const currentTime = getDatetime('YYYY-mm-dd HH:MM:SS'),timeNow = Date.now(); // 开始执行脚本 fs.open(projectsFile + '/' + id + '/' + timeNow + '.log', 'a', function (err, fd) { if (err) { // 操作日志失败了 writeExecLog(id, 'fail'); return; } writeExecLog(id, null, true); // 运行中 let isFailed = -1; const workerProcess = process.spawn('sh', [file]); let startTime = Date.now(); // 记录开始时间 workerProcess.stdout.on('data', (execData) => { let line = execData.toString().trim(); console.log('out:', line); if (line == 'execute success') { isFailed = 1; } else if (line == 'execute fail') { isFailed = 0; } fs.writeSync(fd, line); // 写入日志 }); workerProcess.stderr.on('data', (execData) => { console.log('err:', execData.toString()) fs.writeSync(fd, execData.toString()); // 写入日志 // isFailed = true; }); workerProcess.on('close', function (code) { // 计算使用时间 const useTime = Date.now() - startTime; let status = isFailed == -1 ? '未知' : (isFailed == 1 ? '成功' : '失败'); histories.push({ time: currentTime, timestamp:timeNow, status, useTime }) // 写入记录 fs.writeFileSync(projectsFile + '/' + id + '/index.json', JSON.stringify(histories)); writeExecLog(id, status) fs.close(fd, function (err) { if (err) { console.log('close file error', err); } }); }); }); }); }); //创建项目 router.post('/create', (req, res, next) => { const data = JSON.parse(fs.readFileSync(projectsFile + '/index.json')); let id = data.length + 1; let {name, shell} = req.body; if (!name || !shell) { res.json({code: 1, message: "参数不能为空"}) return; } fs.mkdirSync(projectsFile + '/' + id); // 创建项目目录 // 创建记录文件 fs.writeFile(projectsFile + '/' + id + '/index.json', '[]', function (err) { fs.writeFile(projectsFile + '/' + id + '/build.sh', shell, (err1) => { if (err1) { console.log('create shell success') } }); if (err) { res.json({code: 2, message: "创建项目失败"}) } data.push({ id, name, create: getDatetime(), lastUpdate: null, lastStatus: "", running: false }) fs.writeFile(projectsFile + '/index.json', JSON.stringify(data), (err1) => { if (err1) { console.log('create project fail') } }) res.json(data); }); }); module.exports = router;