UNPKG

monaca-lib

Version:

Monaca cloud API bindings for JavaScript

288 lines (233 loc) 8.11 kB
(function() { 'use strict'; // imports var qs = require('querystring'), path = require('path'), fs = require('fs'), rc4 = require(path.join(__dirname, 'rc4')); var PAIRING_KEYS_FILE = path.join( process.env[(process.platform == 'win32') ? 'USERPROFILE' : 'HOME'], '.cordova', 'monaca_pairing_keys.json' ); var Api = function(localkit) { this.localkit = localkit; this.monaca = localkit.monaca; this.routes = { '/api/pairing/request': this.pairingApi.bind(this), '/api/projects': this.projectsApi.bind(this), '/api/project/:project_id/file/tree': this.fileTreeApi.bind(this), '/api/project/:project_id/file/read': this.fileReadApi.bind(this), '/api/debugger/inspect': this.inspectApi.bind(this) }; }; Api.prototype.sendJsonResponse = function(response, code, message, result, encrypt, pairingKey) { response.writeHead(code, { 'Content-Type': 'application/json' }); var data = { status: code.toString()[0] === '2' ? 'ok' : 'fail', code: code, message: message }; if (result) { data.result = result; } var msg = JSON.stringify(data); if (encrypt) { response.end(rc4.encrypt(msg, pairingKey)); } else { response.end(msg); } }; Api.prototype.matchRoutes = function(url, route) { var urlParts = url.split('?')[0].split('/'), routeParts = route.split('/'); if (urlParts.length !== routeParts.length) { return null; } var data = {}; for (var i = 0, l = urlParts.length; i < l; i ++) { var urlPart = urlParts[i], routePart = routeParts[i]; if (routePart.charAt(0) === ':') { data[routePart.substr(1)] = urlPart; } else { if (routePart !== urlPart) { return null; } } } return data; }; Api.prototype.getPairingKey = function(request) { var clientId = request.headers['x-monaca-client-id-hash']; return this.localkit.pairingKeys[clientId]; }; Api.prototype.requestHandler = function(request, response) { var keys = Object.keys(this.routes); for (var i = 0, l = keys.length; i < l; i ++) { var data = this.matchRoutes(request.url, keys[i]); if (data) { return this.routes[keys[i]](request, response, data); } } this.sendJsonResponse(response, 404, 'No such route.'); }; Api.prototype.pairingApi = function(request, response) { var data = qs.parse((request.url + '?').split('?')[1]), requestToken = data.requestToken, clientIdHash = data.clientIdHash; if (!requestToken || !clientIdHash) { if (this.localkit.verbose) { console.log('Invalid pairing paramters.'); } return this.sendJsonResponse(400, 'Must specify request token and client id hash.'); } else { this.localkit.requestPairingKey(requestToken, clientIdHash).then( function(pairingKey) { if (this.localkit.verbose) { console.log('Paired with debugger!'); } this.localkit.pairingKeys[clientIdHash] = pairingKey; fs.writeFile(PAIRING_KEYS_FILE, JSON.stringify(this.localkit.pairingKeys), function(error) { if (error) { return this.sendJsonResponse(response, 500, 'Unable to save pairing key.'); } return this.sendJsonResponse(response, 200, 'Received pairing key.', this.localkit._getServerInfo()); }.bind(this)); }.bind(this), function(error) { if (this.localkit.verbose) { console.log('Failed pairing with debugger.'); } return this.sendJsonResponse(response, 400, error); }.bind(this) ); } }; Api.prototype.validatePairing = function(request) { var pairingKey = this.getPairingKey(request); if (!pairingKey) { if (this.localkit.verbose) { console.log('No pairing key associated with this device.'); } return false; } var remotePairingKey; try { remotePairingKey = rc4.decrypt(request.headers['x-monaca-client-credential'], pairingKey); } catch (error) { remotePairingKey = null; } if (remotePairingKey === pairingKey) { return true; } if (this.localkit.verbose) { console.log('Invalid pairing key.'); } return false; }; Api.prototype.projectsApi = function(request, response) { if (!this.validatePairing(request)) { this.sendJsonResponse(response, 401, 'Not paired with debugger.'); return; } var pairingKey = this.getPairingKey(request); if (this.localkit.verbose) { console.log('Debugger requested project list.'); } this.localkit.getProjects().then( function(projects) { this.sendJsonResponse(response, 200, 'Project list', projects, true, pairingKey); }.bind(this), function(error) { this.sendJsonResponse(response, 400, 'Unable to get project list.', undefined, true, pairingKey); }.bind(this) ); }; Api.prototype.fileTreeApi = function(request, response, data) { if (!this.validatePairing(request)) { this.sendJsonResponse(response, 401, 'Not paired with debugger.'); return; } var pairingKey = this.getPairingKey(request); var projectId = data.project_id; if (this.localkit.verbose) { console.log('Debugger requested file tree.'); } this.localkit.getProjectFiles(projectId).then( function(files) { this.sendJsonResponse(response, 200, 'File list', {items: files}, true, pairingKey); }.bind(this), function(error) { this.sendJsonResponse(response, 400, 'Unable to get project files.', undefined, true, pairingKey); }.bind(this) ); }; Api.prototype.fileReadApi = function(request, response, data) { if (!this.validatePairing(request)) { this.sendJsonResponse(response, 401, 'Not paired with debugger.'); return; } var pairingKey = this.getPairingKey(request); var projectId = data.project_id, body = ''; request.on('data', function (data) { body += data; if (body.length > 1e6) { request.connection.destroy(); } }); request.on('end', function () { try { var data = JSON.parse(rc4.decrypt(body, pairingKey)); if (this.localkit.verbose) { console.log('Debugger requested file: ' + data.path); } this.localkit.readProjectFile(projectId, data.path).then( function(buf) { response.writeHead(200, { 'Content-Type': 'application/octet-stream' }); response.end(rc4.encrypt(buf, pairingKey)); }.bind(this), function(error) { this.sendJsonResponse(response, 500, 'Unable to read file.', undefined, true, pairingKey); }.bind(this) ); } catch (error) { this.sendJsonResponse(response, 400, 'Unable to parse data', undefined, true, pairingKey); } }.bind(this)); }; Api.prototype.inspectApi = function(request, response) { if (!this.validatePairing(request)) { this.sendJsonResponse(response, 401, 'Not paired with debugger.'); return; } var data = qs.parse((request.url + '?').split('?')[1]), pairingKey = this.getPairingKey(request), fileUrl = data.fileUrl; if (!fileUrl) { return this.sendJsonResponse(response, 400, 'Parameters missing.', undefined, true, pairingKey); } var platform = data.hasOwnProperty('abstractSocketAddress') ? 'android' : 'ios'; this.localkit.startInspector({ type: platform, pageUrl: fileUrl }).then( function() { return this.sendJsonResponse(response, 200, 'Inspection started.', undefined, true, pairingKey); }.bind(this), function(error) { return this.sendJsonResponse(response, 500, 'Unable to start inspector: ' + error, undefined, true, pairingKey); }.bind(this) ); } module.exports = Api; })();