UNPKG

mya-server-jinja

Version:

mya server jinja

228 lines (190 loc) 6.78 kB
const path = require('path'); const fs = require('fs'); const { spawn } = require('child_process'); const util = fis.require('command-server/lib/util.js'); const tar = require('tar'); const version = require('./package.json').version; /** * opt: { type: 'jinja', root: '~/.mya-tmp/www', port: 8080, timeout: 30, browse: true, daemon: false, https: false } */ function start(opt, callback) { process.stdout.write('starting fis-server .'); const timeout = Math.max(opt.timeout * 1000, 5000); delete opt.timeout; let ready = false, log = '', timeoutTimer; let args = [ path.join(opt.root, 'flask_server', 'application.py'), '--debug' // 强制开启debug模式 ]; // copy mya server start 参数 fis.util.map(opt, function(key, value) { args.push('--' + key, String(value)); }); const server = spawn('python', args, { detached: opt.daemon }); function open() { const protocol = opt.https ? "https" : "http"; const port = opt.port == 80 ? '/' : ':' + opt.port + '/'; const address = `${protocol}://127.0.0.1${port}`; fis.log.notice('Browse ' + '%s'.yellow.bold, address); fis.log.notice('Or browse ' + '%s'.yellow.bold, `${protocol}://${util.hostname}${port}`); opt.browse ? util.open(address, function() { opt.daemon && process.exit(); }) : (opt.daemon && process.exit()); } server.stderr.on('data', chunk => { if (ready) return; chunk = chunk.toString('utf8'); log += chunk; process.stdout.write('.'); if (~chunk.indexOf('Running on')) { ready = true; clearTimeout(timeoutTimer); process.stdout.write(' at port [' + opt.port + ']\n'); // write pid to file util.pid(server.pid, 'jinja', opt.port); // flask 开启debug模式时,会起两个进程,要kill分组下的所有进程 ( kill -pid ) setTimeout(open, 200); } else if (~chunk.indexOf('socket.error')) { process.stdout.write(' fail\n'); let errMsg = 'unknown'; if (~chunk.indexOf('Address already in use')) { errMsg = 'Address already in use:' + opt.port; } callback(errMsg); } }); server.on('error', err => { try { process.kill(-server.pid, 'SIGKILL') } catch (e) {} fis.log.error(err) }); if (opt.daemon) { // By default, the parent will wait for the detached child to exit. // To prevent the parent from waiting for a given child, use the child.unref() method. server.unref(); timeoutTimer = setTimeout(function() { process.stdout.write(' fail\n'); if (log) console.log(log); fis.log.error('timeout'); }, timeout); } else { // 将server的输出流通过管道传递到父进程 server.stdout.pipe(process.stdout); server.stderr.pipe(process.stderr); } } function extract(source, target, callback) { fs .createReadStream(source) .pipe(tar.Extract({ path: target })) .on('error', function(err) { fis.log.error('extract tar file [%s] fail, error [%s]', source, err); }) .on('end', function() { // copy -r flask_server/template ../ fis.util.copy(path.join(target, 'flask_server', 'template'), path.join(target, 'template')); // copy -r flask_server/resource ../ fis.util.copy(path.join(target, 'flask_server', 'resource'), path.join(target, 'resource')); callback && callback(); }); } function checkPythonEnable(opt, callback) { let pythonVersion = false; const check = function(data) { if (pythonVersion) { return ; } pythonVersion = util.matchVersion(data.toString('utf8')); pythonVersion && process.stdout.write('v' + pythonVersion + '\n'); }; // check python process.stdout.write('checking python support : '); const python = spawn('python', ['--version']); python.stdout.on('data', check); python.stderr.on('data', check); python.on('error', function() { process.stdout.write('unsupported python environment\n'); callback(pythonVersion); }); python.on('exit', function() { callback(pythonVersion); }); } function semver2num(version) { return + version.trim().replace(/\./g, ''); } exports.start = function(opt, callback) { checkPythonEnable(opt, function(pythonVersion) { if (!pythonVersion) { callback('`python` is required.'); return ; } const flaskServer = path.join(opt.root, 'flask_server'); const versionConf = path.join(flaskServer, 'version.conf'); let curVersion = 0; if (fis.util.exists(versionConf)) { curVersion = fis.util.read(versionConf); } // 无当前版本,或者当前版本小于包版本,清空目录,生成版本文件 if (!curVersion || semver2num(curVersion) !== semver2num(version)) { fis.util.del(flaskServer); } if (!fis.util.isDir(flaskServer)) { fis.util.write(versionConf, version, 'utf-8'); extract(path.join(__dirname, 'vendor', 'flask_server.tar'), opt.root, function() { start(opt, callback); }); } else { start(opt, callback); } }); }; exports.clean = function(opt) { var argExclude = opt.exclude || []; if (!Array.isArray(argExclude)) { argExclude = [argExclude]; } // merge command args // var exclude = [ // 'click/**', // 'flask/**', // 'jinja2/**', // 'lib/**', // 'markupsafe/**', // 'mya_jinja/**', // 'rewrite/**', // 'werkzeug/**', // '__init__.py', // 'application.py', // 'itsdangerous.py', // 'settings.py', // 'ujson.so', // 'flask.log', // 'version.conf' // ].map(path => `/flask_server/${path}`).concat(argExclude); var exclude = [ '/flask_server/**', '/resource/lib/**', '/template/*.html' ].concat(argExclude); // because fis.util.glob beginning with `^/`,so need fix it. exclude = exclude.map(function(pattern) { var reg = fis.util.glob(pattern); return new RegExp('^' + fis.util.escapeReg(opt.root) + reg.source.substring(1)); }); fis.util.del(opt.root, opt.include || fis.get('server.clean.include'), opt.exclude || exclude || fis.get('server.clean.exclude') ); };