UNPKG

minxing-tools-core

Version:
482 lines (444 loc) 13.2 kB
'use strict'; const WebSocketServer = require('ws').Server const path = require('path') const URL = require('url'); const fse = require('fs-extra') const EventEmitter = require('events') const CLI_COMMAND = -1 const glob = require("glob") const Utils = require('../utils/utils'); const MXBuild = require('./build'); const Package = require('./package') const WifiSync = { port: null, tempPath: null, socketServer: null, workspace: null, httpServer: null, clientsCount: 0, emitter: new EventEmitter(), localIp() { var os = require('os'), ifaces = os.networkInterfaces(); let address = [] for (var dev in ifaces) { ifaces[dev].forEach(function (details, alias) { if ("IPv4" === details.family && details.address !== "127.0.0.1") { address.push(details.address) } }) } return address }, start({ tempPath, port = 8686 }) { const server = require('http').createServer((req, res) => { let urlPath = req.url const pathname = URL.parse(req.url).pathname; const zipPath = path.join(this.tempPath, pathname); if (!fse.existsSync(zipPath)) { this.notFound({ res: res }) return; } const stat = fse.statSync(zipPath); res.setHeader('Accept-Ranges', 'bytes'); if (req.headers['range']) { const range = Utils.parseRange(req.headers["range"], stat.size); if (range) { res.setHeader("Content-Range", "bytes " + range.start + "-" + range.end + "/" + stat.size); res.setHeader("Content-Length", (range.end - range.start + 1)); fse.createReadStream(zipPath, { "start": range.start, "end": range.end }).pipe(res); } else { res.removeHeader("Content-Length"); res.writeHead(416, "Request Range Not Satisfiable"); res.end(); } } else { res.setHeader('Content-Length', stat.size); fse.createReadStream(zipPath).pipe(res); } }) const wss = new WebSocketServer({ server: server }) this.port = port; this.socketServer = wss; this.httpServer = server; this.tempPath = tempPath; wss.on('connection', (socket) => { this.handleConnection({ socket: socket }) }) server.listen(port, () => { console.log(`敏行 Is Listening on ip: ${JSON.stringify(this.localIp())} port: ${server.address().port})`) }) }, end({}) { this.socketServer.close() this.httpServer.close() console.log("敏行 Wifi 真机同步服务 已关闭...") }, handleConnection({ socket }) { ++this.clientsCount console.log("connection") console.log(`当前连接设备数:${this.clientsCount}, port:${this.port}`) /* http服务端的监听端口,下载文件、实时预览时使用该端口. */ socket.send(JSON.stringify({ command: 7, port: this.port })) socket.on("error", (err) => { // silent... }) socket.on('close', () => { console.log('disconnected'); --this.clientsCount console.log(`当前连接设备数:${this.clientsCount}`) }); socket.on("message", (message) => { let receiveCmd = ''; try { receiveCmd = JSON.parse(message); } catch (e) { console.log('JSON parse error->', e); receiveCmd = message; } console.log('socket.on message->', receiveCmd); receiveCmd.command = receiveCmd.command - 0; if (CLI_COMMAND === receiveCmd.command) { // cli 保留编号. let { payload } = receiveCmd payload = payload || {} payload["params"]["socket"] = socket this.handleCli(payload) } if (4 === receiveCmd.command) { let cmd = this.replyLoaderHeartbeatCmd({ command: receiveCmd.command }) this.sendCommand({ socket: socket, cmd: cmd }) } if (5 === receiveCmd.command) { heartbeatTimes-- } if (2 === receiveCmd.command) { Package.getDownloadCmd({ appId: receiveCmd.appId, timestamp: receiveCmd.timestamp, workspace: this.workspace, tempPath: this.tempPath }) .then(cmd => { console.log('response 2-->', cmd); if (cmd) { this.sendCommand({ socket: socket, cmd: cmd }) } }) } if (8 === receiveCmd.command) { this.handleLog({ cmd: receiveCmd }) } }) }, sync({ project, updateAll }) { // 更新,全量或增量. if (typeof project !== "string") { console.log(`${project} 不是一个有效的文件路径`) return } let projectPath = path.resolve(project); let configPath = path.resolve(projectPath, "plugin.properties"); let workspace = path.resolve(projectPath, ".."); let appId = null if (fse.existsSync(configPath)) { const appInfo = Utils.readPropertiesSync(configPath); if (appInfo) { appId = appInfo['app_id']; } } if (!appId) { console.log(`${project} 似乎不是一个有效的敏行项目`); return } this.workspace = workspace let cmd = this.syncCmd({ appId: appId, updateAll: updateAll }) this.broadcastCommand({ socketServer: this.socketServer, cmd: cmd }) }, preview({ file }) { // 页面实时预览. if (typeof file !== "string") { console.log(`${file} 不是一个有效的文件路径`) return } file = path.resolve(file) // 逆序寻找最接近目标文件的plugin.properties文件. let project = path.resolve(file, "..") let configPath = null let appId = null for (; true;) { configPath = path.resolve(project, "plugin.properties"); if (fse.existsSync(configPath)) { appId = Utils.getAppId(configPath); break } if (project === path.resolve("/")) { break } project = path.resolve(project, "..") } if (!appId) { console.log(`${file} 似乎不在有效的敏行项目中`) return } this.workspace = path.resolve(project, "..") let cmd = this.previewCmd({ file: file, workspace: this.workspace, appId: appId }) this.broadcastCommand({ socketServer: this.socketServer, cmd: cmd }) }, broadcastCommand({ socketServer, cmd }) { // 广播. console.log('broadcastCommand cmd->', cmd); socketServer.clients.forEach((socket) => { this.sendCommand({ socket: socket, cmd: cmd }) }) }, sendCommand({ socket, cmd }) { let cmdStr = JSON.stringify(cmd) socket.send(cmdStr, (error) => { // silent... }) }, handleLog({ cmd }) { this.emitter.emit('log', { content: cmd.content, level: cmd.level }) }, on(event, callback) { this.emitter.on(event, callback) }, syncCmd({ appId, updateAll = true }) { // 发送‘wifi同步测试’指令 return { command: 1, appId: appId, //当前应用id updateAll: updateAll, //是否全量更新 } }, globMatch({ project, sync = ".syncignore" }) { // 读取 .syncignore 忽略后的文件. let syncPath = path.resolve(project, sync) let syncignore = (fse.existsSync(syncPath) && fse.readFileSync(syncPath, { encoding: "utf8" })) || "" let globmaths = glob.sync("**", { nodir: true, ignore: syncignore, realpath: true, cwd: project, }) return globmaths } ,replyLoaderHeartbeatCmd({ command }) { // 响应 APPLoader 发送的心跳包 return { command: command } }, previewCmd({ file, workspace, appId }) { // 发送‘页面实时预览’指令 let absoluteUrlPath = this.absoluteUrlPath({ file: file, workspace: workspace, appId: appId }) return { command: 6, path: absoluteUrlPath, appId: appId } }, httpPortCmd({ port }) { // 返回 http 端口信息. return { command: 7, port: port // http服务端的监听端口,下载文件、实时预览时使用该端口 } }, absoluteUrlPath({ file, workspace, appId }) { // 本地文件对应的服务器地址. console.log('work space->', workspace); let relativePath = path.relative(workspace, file) relativePath = relativePath.replace(/\\/g, "/") let absoluteUrlPath = `/${relativePath.replace(/^[^\/]*/,appId)}` return absoluteUrlPath }, projectPath({ appId, workspace }) { // 特定appId对应的项目的根目录. let projectPath = null return new Promise((resolve) => { fse.walk(workspace, { filter: (file) => { return path.resolve(workspace) === path.resolve(file, "./..") } }) .on('data', (item) => { let itemPath = item.path let itemStats = item.stats let configFilePath = path.join(itemPath, "plugin.properties") if (!itemStats.isDirectory() || !fse.existsSync(configFilePath)) { return } const app_id = Utils.getAppId(configFilePath); if (appId === app_id) { resolve(itemPath) } }) .on('end', function () { resolve(projectPath) }) }) }, notFound({ res }) { // 404 res.writeHead(404, { "Content-Type": "text/plain" }) res.write("404 Not found") res.end() }, handleCli({ method, params }) { const { socket } = params if (typeof CLI[method] === "function") { CLI[method](params) } else { console.log(`不支持的方法: ${method}`) socket.close() } }, } const CLI = { wifiSync({ project = "./", updateAll = true, socket }) { WifiSync.sync({ project: project, updateAll: updateAll }) socket.close() }, wifiStop({ socket }) { WifiSync.end({}) }, wifiPreview({ file, socket }) { WifiSync.preview({ file: file }) socket.close() }, wifiInfo({ socket }) { const wifiInfo = { ip: WifiSync.localIp(), port: WifiSync.port, clientsCount: WifiSync.clientsCount } let cmd = { command: CLI_COMMAND, payload: wifiInfo } WifiSync.sendCommand({ socket, cmd }) socket.close() }, wifiLog({ socket }) { WifiSync.on("log", (log) => { let cmd = { command: CLI_COMMAND, payload: log } WifiSync.sendCommand({ socket, cmd }) }) } } module.exports = WifiSync