mya-server-jinja
Version:
mya server jinja
228 lines (190 loc) • 6.78 kB
JavaScript
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')
);
};