From d67863cff602c69120b14f638566e8042880038c Mon Sep 17 00:00:00 2001 From: Anthony Laibe Date: Mon, 7 Jan 2019 16:13:48 +0000 Subject: [PATCH] feat: add API server --- embark-ui/package.json | 1 - embark-ui/src/components/Accounts.js | 2 +- embark-ui/src/components/Blocks.js | 2 +- embark-ui/src/components/Contracts.js | 2 +- embark-ui/src/components/ContractsList.js | 2 +- embark-ui/src/components/DebugButton.js | 2 +- embark-ui/src/components/ExplorerLayout.js | 16 +-- embark-ui/src/components/Layout.js | 42 +++---- embark-ui/src/components/Transaction.js | 2 +- embark-ui/src/components/Transactions.js | 2 +- embark-ui/src/components/UtilsLayout.js | 10 +- embark-ui/src/constants.js | 2 +- embark-ui/src/routes.js | 12 +- package.json | 7 +- src/cmd/cmd_controller.js | 2 + src/lib/core/engine.js | 9 +- src/lib/modules/api/index.ts | 109 +++++++++++++++++ src/lib/modules/api/server.ts | 130 +++++++++++++++++++++ src/lib/modules/authenticator/index.js | 15 +-- src/lib/modules/webserver/index.js | 27 +---- src/lib/modules/webserver/server.js | 48 -------- src/lib/utils/network.ts | 10 ++ src/typings/embark.d.ts | 9 ++ yarn.lock | 82 +++++++++++++ 24 files changed, 414 insertions(+), 131 deletions(-) create mode 100644 src/lib/modules/api/index.ts create mode 100644 src/lib/modules/api/server.ts create mode 100644 src/lib/utils/network.ts diff --git a/embark-ui/package.json b/embark-ui/package.json index b1d6671ac..9691caf15 100644 --- a/embark-ui/package.json +++ b/embark-ui/package.json @@ -92,7 +92,6 @@ "start": "node scripts/start.js", "test": "node scripts/test.js" }, - "homepage": "http://localhost:8000/embark", "eslintConfig": { "extends": "react-app", "rules": { diff --git a/embark-ui/src/components/Accounts.js b/embark-ui/src/components/Accounts.js index 36aea7be3..699d04e30 100644 --- a/embark-ui/src/components/Accounts.js +++ b/embark-ui/src/components/Accounts.js @@ -16,7 +16,7 @@ const Accounts = ({accounts}) => ( {accounts.map(account => (
Account  - {account.address} + {account.address} diff --git a/embark-ui/src/components/Blocks.js b/embark-ui/src/components/Blocks.js index 757730256..d301f41a3 100644 --- a/embark-ui/src/components/Blocks.js +++ b/embark-ui/src/components/Blocks.js @@ -17,7 +17,7 @@ const Blocks = ({blocks, changePage, currentPage, numberOfPages}) => ( {blocks.map(block => (
Block  - + {block.number} diff --git a/embark-ui/src/components/Contracts.js b/embark-ui/src/components/Contracts.js index fa1fdc5d7..7eaddba76 100644 --- a/embark-ui/src/components/Contracts.js +++ b/embark-ui/src/components/Contracts.js @@ -21,7 +21,7 @@ const Contracts = ({contracts, title = "Contracts"}) => ( return (
- {contract.className} + {contract.className} diff --git a/embark-ui/src/components/ContractsList.js b/embark-ui/src/components/ContractsList.js index 2b9c7880a..fe5600817 100644 --- a/embark-ui/src/components/ContractsList.js +++ b/embark-ui/src/components/ContractsList.js @@ -19,7 +19,7 @@ const ContractsList = ({contracts}) => ( const contractDisplay = formatContractForDisplay(contract); return ( - {contract.className} + {contract.className} {contractDisplay.address} {contractDisplay.state} diff --git a/embark-ui/src/components/DebugButton.js b/embark-ui/src/components/DebugButton.js index 5d4012e65..a44fc0c1b 100644 --- a/embark-ui/src/components/DebugButton.js +++ b/embark-ui/src/components/DebugButton.js @@ -7,7 +7,7 @@ import {withRouter} from "react-router-dom"; class DebugButton extends React.Component { onClick() { - this.props.history.push(`/embark/editor?debuggerTransactionHash=${this.props.transaction.hash}`); + this.props.history.push(`/editor?debuggerTransactionHash=${this.props.transaction.hash}`); this.props.onClick(); } diff --git a/embark-ui/src/components/ExplorerLayout.js b/embark-ui/src/components/ExplorerLayout.js index 59814cb8a..93156ddb3 100644 --- a/embark-ui/src/components/ExplorerLayout.js +++ b/embark-ui/src/components/ExplorerLayout.js @@ -13,14 +13,14 @@ import TransactionContainer from '../containers/TransactionContainer'; const ExplorerLayout = () => ( - - - - - - - - + + + + + + + + ); diff --git a/embark-ui/src/components/Layout.js b/embark-ui/src/components/Layout.js index e784abd3b..afcf6f403 100644 --- a/embark-ui/src/components/Layout.js +++ b/embark-ui/src/components/Layout.js @@ -39,27 +39,27 @@ import logo from '../images/logo-new.svg'; import './Layout.css'; const HEADER_NAV_ITEMS = [ - {name: "Dashboard", to: "/embark", icon: 'tachometer'}, - {name: "Deployment", to: "/embark/deployment", icon: "arrow-up"}, - {name: "Explorer", to: "/embark/explorer/overview", icon: "compass"}, - {name: "Editor", to: "/embark/editor", icon: "codepen"}, - {name: "Utils", to: "/embark/utilities/converter", icon: "cog"} + {name: "Dashboard", to: "/", icon: 'tachometer'}, + {name: "Deployment", to: "/deployment", icon: "arrow-up"}, + {name: "Explorer", to: "/explorer/overview", icon: "compass"}, + {name: "Editor", to: "/editor", icon: "codepen"}, + {name: "Utils", to: "/utilities/converter", icon: "cog"} ]; const SIDEBAR_NAV_ITEMS = { - "/embark/explorer" : {items: [ - {url: "/embark/explorer/overview", icon: "fa fa-signal", name: "Overview"}, - {url: "/embark/explorer/accounts", icon: "fa fa-users", name: "Accounts"}, - {url: "/embark/explorer/blocks", icon: "fa fa-stop", name: "Blocks"}, - {url: "/embark/explorer/contracts", icon: "fa fa-file-code-o", name: "Contracts"}, - {url: "/embark/explorer/transactions", icon: "fa fa-exchange", name: "Transactions"} + "/explorer" : {items: [ + {url: "/explorer/overview", icon: "fa fa-signal", name: "Overview"}, + {url: "/explorer/accounts", icon: "fa fa-users", name: "Accounts"}, + {url: "/explorer/blocks", icon: "fa fa-stop", name: "Blocks"}, + {url: "/explorer/contracts", icon: "fa fa-file-code-o", name: "Contracts"}, + {url: "/explorer/transactions", icon: "fa fa-exchange", name: "Transactions"} ]}, - "/embark/utilities/": {items: [ - {url: "/embark/utilities/converter", icon: "fa fa-plug", name: "Converter"}, - {url: "/embark/utilities/communication", icon: "fa fa-phone", name: "Communication"}, - {url: "/embark/utilities/ens", icon: "fa fa-circle", name: "ENS"}, - {url: "/embark/utilities/sign-and-verify", icon: "fa fa-edit", name: "Sign & Verify"}, - {url: "/embark/utilities/transaction-decoder", icon: "fa fa-edit", name: "Transaction Decoder"} + "/utilities/": {items: [ + {url: "/utilities/converter", icon: "fa fa-plug", name: "Converter"}, + {url: "/utilities/communication", icon: "fa fa-phone", name: "Communication"}, + {url: "/utilities/ens", icon: "fa fa-circle", name: "ENS"}, + {url: "/utilities/sign-and-verify", icon: "fa fa-edit", name: "Sign & Verify"}, + {url: "/utilities/transaction-decoder", icon: "fa fa-edit", name: "Transaction Decoder"} ]} }; @@ -96,19 +96,19 @@ class Layout extends React.Component { } if (nextProps.searchResult.className) { - this.props.history.push(`/embark/explorer/contracts/${nextProps.searchResult.className}`); + this.props.history.push(`/explorer/contracts/${nextProps.searchResult.className}`); return false; } if (nextProps.searchResult.address) { - this.props.history.push(`/embark/explorer/accounts/${nextProps.searchResult.address}`); + this.props.history.push(`/explorer/accounts/${nextProps.searchResult.address}`); return false; } if (nextProps.searchResult.hasOwnProperty('transactionIndex')) { - this.props.history.push(`/embark/explorer/transactions/${nextProps.searchResult.hash}`); + this.props.history.push(`/explorer/transactions/${nextProps.searchResult.hash}`); return false; } if (nextProps.searchResult.hasOwnProperty('number')) { - this.props.history.push(`/embark/explorer/blocks/${nextProps.searchResult.number}`); + this.props.history.push(`/explorer/blocks/${nextProps.searchResult.number}`); return false; } // Returned something we didn't know existed diff --git a/embark-ui/src/components/Transaction.js b/embark-ui/src/components/Transaction.js index b94ce3362..f36a364d4 100644 --- a/embark-ui/src/components/Transaction.js +++ b/embark-ui/src/components/Transaction.js @@ -23,7 +23,7 @@ const Transaction = ({transaction, contracts}) => (
- {transaction.blockNumber}} /> + {transaction.blockNumber}} /> diff --git a/embark-ui/src/components/Transactions.js b/embark-ui/src/components/Transactions.js index 6cc7baaf9..643466bb8 100644 --- a/embark-ui/src/components/Transactions.js +++ b/embark-ui/src/components/Transactions.js @@ -18,7 +18,7 @@ const Transactions = ({transactions, contracts, changePage, currentPage, numberO {transactions.map(transaction => (
Transaction  - + {transaction.hash} diff --git a/embark-ui/src/components/UtilsLayout.js b/embark-ui/src/components/UtilsLayout.js index dbca42ced..90735959a 100644 --- a/embark-ui/src/components/UtilsLayout.js +++ b/embark-ui/src/components/UtilsLayout.js @@ -9,11 +9,11 @@ import TransactionDecoderContainer from '../containers/TransactionDecoderContain const UtilsLayout = () => ( - - - - - + + + + + ); diff --git a/embark-ui/src/constants.js b/embark-ui/src/constants.js index 10c17b5a9..97596ca38 100644 --- a/embark-ui/src/constants.js +++ b/embark-ui/src/constants.js @@ -7,7 +7,7 @@ export const DEPLOYMENT_PIPELINES = { injectedWeb3: 'injectedWeb3', embark: 'embark' }; -export const DEFAULT_HOST = process.env.NODE_ENV === 'development' ? 'localhost:8000' : window.location.host; +export const DEFAULT_HOST = 'localhost:55555'; export const OPERATIONS = { MORE: 1, LESS: -1 diff --git a/embark-ui/src/routes.js b/embark-ui/src/routes.js index 79163566e..a800a2f53 100644 --- a/embark-ui/src/routes.js +++ b/embark-ui/src/routes.js @@ -12,12 +12,12 @@ import UtilsLayout from './components/UtilsLayout'; const routes = ( - - - - - - + + + + + + diff --git a/package.json b/package.json index defa1bdc1..84e5e61b5 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,6 @@ "@babel/preset-react": "7.0.0", "@babel/preset-typescript": "7.1.0", "@babel/runtime-corejs2": "7.1.2", - "@types/pretty-ms": "3.2.0", "ajv": "6.5.5", "ascii-table": "0.0.9", "async": "2.6.1", @@ -192,11 +191,17 @@ "@babel/cli": "7.1.2", "@babel/plugin-proposal-optional-chaining": "7.0.0", "@types/async": "2.0.50", + "@types/body-parser": "1.17.0", + "@types/cors": "2.8.4", + "@types/express": "4.16.0", + "@types/express-ws": "3.0.0", "@types/globule": "1.1.3", "@types/handlebars": "4.0.39", + "@types/helmet": "0.0.42", "@types/i18n": "0.8.3", "@types/node": "10.11.7", "@types/os-locale": "2.1.0", + "@types/pretty-ms": "3.2.0", "@types/web3": "1.0.12", "babel-plugin-dynamic-import-node": "2.2.0", "chai": "4.1.2", diff --git a/src/cmd/cmd_controller.js b/src/cmd/cmd_controller.js index a4a051f66..cee754ceb 100644 --- a/src/cmd/cmd_controller.js +++ b/src/cmd/cmd_controller.js @@ -127,6 +127,7 @@ class EmbarkController { engine.startService("storage"); engine.startService("codeGenerator"); engine.startService("console"); + engine.startService("cockpit"); engine.startService("pluginCommand"); engine.events.on('check:backOnline:Ethereum', function () { @@ -300,6 +301,7 @@ class EmbarkController { engine.startService("storage"); engine.startService("codeGenerator"); engine.startService("console"); + engine.startService("cockpit"); engine.startService("pluginCommand"); engine.events.once('check:backOnline:Ethereum', () => callback()); }, diff --git a/src/lib/core/engine.js b/src/lib/core/engine.js index 20f7cd6d2..907d612bf 100644 --- a/src/lib/core/engine.js +++ b/src/lib/core/engine.js @@ -61,6 +61,7 @@ class Engine { let services = { "serviceMonitor": this.serviceMonitor, "pipeline": this.pipelineService, + "cockpit": this.cockpitService, "codeRunner": this.codeRunnerService, "codeGenerator": this.codeGeneratorService, "compiler": this.setupCompilerAndContractsManagerService, @@ -172,7 +173,6 @@ class Engine { config: this.config, forceRegister: options.forceRegister }); - this.registerModule('authenticator'); } codeRunnerService(_options) { @@ -257,8 +257,13 @@ class Engine { this.events.request('watcher:start'); } + cockpitService() { + this.registerModule('authenticator'); + this.registerModule('api', {plugins: this.plugins}); + } + webServerService() { - this.registerModule('webserver', {plugins: this.plugins}); + this.registerModule('webserver'); } storageService(_options) { diff --git a/src/lib/modules/api/index.ts b/src/lib/modules/api/index.ts new file mode 100644 index 000000000..f6316a7f6 --- /dev/null +++ b/src/lib/modules/api/index.ts @@ -0,0 +1,109 @@ +import { Embark } from "../../../typings/embark"; +import {canonicalHost} from "../../utils/host.js"; +import {findNextPort} from "../../utils/network"; +import Server from "./server"; + +const utils = require("../../utils/utils.js"); + +const DEFAULT_PORT = 55555; + +export default class Api { + private port!: number; + private api!: Server; + private apiUrl!: string; + + constructor(private embark: Embark, private options: any) { + this.embark.events.emit("status", __("Starting API")); + findNextPort(DEFAULT_PORT).then((port) => { + this.port = port; + this.apiUrl = "http://" + canonicalHost("127.0.0.1") + ":" + this.port; + + this.api = new Server(this.embark, this.port, options.plugins); + + this.listenToCommands(); + this.registerConsoleCommands(); + + this.embark.events.request("processes:register", "api", { + launchFn: (cb: (error: Error | null, message: string) => void) => { + this.api.start() + .then(() => cb(null, __("API available at %s", this.apiUrl))) + .catch((error: Error) => cb(error, "")); + }, + stopFn: (cb: (error: Error | null, message: string) => void) => { + this.api.stop() + .then(() => cb(null, __("API stopped"))) + .catch((error: Error) => cb(error, "")); + }, + }); + + this.embark.events.request("processes:launch", "api", (error: Error | null, message: string) => { + if (error) { + this.embark.logger.error(error.message); + } else { + this.embark.logger.info(message); + } + this.setServiceCheck(); + }); + }); + } + + private setServiceCheck() { + this.embark.events.request("services:register", "api", (cb: (options: object) => any) => { + utils.checkIsAvailable(this.apiUrl, (isAvailable: boolean) => { + const devServer = __("API") + " (" + this.apiUrl + ")"; + const serverStatus = (isAvailable ? "on" : "off"); + return cb({name: devServer, status: serverStatus}); + }); + }); + + this.embark.events.on("check:wentOffline:api", () => { + this.embark.logger.info(__("API is offline")); + }); + } + + private listenToCommands() { + this.embark.events.setCommandHandler("api:url", (cb) => cb(this.apiUrl)); + this.embark.events.setCommandHandler("start-api", (cb) => this.embark.events.request("processes:launch", "api", cb)); + this.embark.events.setCommandHandler("stop-api", (cb) => this.embark.events.request("processes:stop", "api", cb)); + this.embark.events.setCommandHandler("logs:api:turnOn", (cb) => { + this.api.enableLogging(); + cb(); + }); + this.embark.events.setCommandHandler("logs:api:turnOff", (cb) => { + this.api.disableLogging(); + cb(); + }); + } + + private registerConsoleCommands() { + this.embark.registerConsoleCommand({ + description: __("Start or stop the API"), + matches: ["api start"], + process: (cmd: string, callback: () => void) => { + this.embark.events.request("start-api", callback); + }, + usage: "api start/stop", + }); + + this.embark.registerConsoleCommand({ + matches: ["api stop"], + process: (cmd: string, callback: () => void) => { + this.embark.events.request("stop-api", callback); + }, + }); + + this.embark.registerConsoleCommand({ + matches: ["log api on"], + process: (cmd: string, callback: () => void) => { + this.embark.events.request("logs:api:turnOn", callback); + }, + }); + + this.embark.registerConsoleCommand({ + matches: ["log api off"], + process: (cmd: string, callback: () => void) => { + this.embark.events.request("logs:api:turnOff", callback); + }, + }); + } +} diff --git a/src/lib/modules/api/server.ts b/src/lib/modules/api/server.ts new file mode 100644 index 000000000..5714dea19 --- /dev/null +++ b/src/lib/modules/api/server.ts @@ -0,0 +1,130 @@ +import bodyParser from "body-parser"; +import cors from "cors"; +import express, { NextFunction, Request, Response } from "express"; +import expressWs from "express-ws"; +import helmet from "helmet"; +import * as http from "http"; +import * as path from "path"; +import * as ws from "ws"; +import { Embark, Plugins } from "../../../typings/embark"; + +type Method = "get" | "post" | "ws" | "delete"; + +interface CallDescription { + method: Method; + endpoint: string; + cb(req: Request | ws, res: Response | Request): void; +} + +export default class Server { + private isLogging: boolean = false; + private expressInstance: expressWs.Instance; + private server?: http.Server; + + constructor(private embark: Embark, private port: number, private plugins: Plugins) { + this.expressInstance = this.initApp(); + } + + public enableLogging() { + this.isLogging = true; + } + + public disableLogging() { + this.isLogging = false; + } + + public start() { + return new Promise((resolve, reject) => { + if (this.server) { + const message = __("API is already running"); + return reject(new Error(message)); + } + + this.server = this.expressInstance.app.listen(this.port, () => { + resolve(); + }); + }); + } + + public stop() { + return new Promise((resolve, reject) => { + if (!this.server) { + const message = __("API is not running"); + return reject(new Error(message)); + } + + this.server.close(() => { + this.server = undefined; + resolve(); + }); + }); + } + + private initApp() { + const instance = expressWs(express()); + instance.app.use((req: Request, res: Response, next: NextFunction) => { + if (!this.isLogging) { + return next(); + } + + if (!req.headers.upgrade) { + this.embark.logger.info(`API > ${req.method} ${req.originalUrl}`); + } + next(); + }); + + instance.app.use(helmet.noCache()); + instance.app.use(cors()); + + instance.app.use(bodyParser.json()); + instance.app.use(bodyParser.urlencoded({extended: true})); + + instance.app.ws("/logs", (websocket: ws, _req: Request) => { + this.embark.events.on("log", (level: string, message: string) => { + websocket.send(JSON.stringify({msg: message, msg_clear: message.stripColors, logLevel: level}), () => {}); + }); + }); + + if (this.plugins) { + instance.app.get("/embark-api/plugins", (req: Request, res: Response) => { + res.send(JSON.stringify(this.plugins.plugins.map((plugin) => ({name: plugin.name})))); + }); + + const callDescriptions: CallDescription[] = this.plugins.getPluginsProperty("apiCalls", "apiCalls"); + callDescriptions.forEach((callDescription) => this.registerCallDescription(instance, callDescription)); + } + + this.embark.events.on("plugins:register:api", (callDescription: CallDescription) => this.registerCallDescription(instance, callDescription)); + + const ui = express.static(path.join(__dirname, "../../../../embark-ui/build")); + instance.app.use("/", ui); + instance.app.use("/*", ui); + + return instance; + } + + private registerCallDescription(instance: expressWs.Instance, callDescription: CallDescription) { + if (callDescription.method === "ws") { + instance.app.ws(callDescription.endpoint, this.applyWSFunction.bind(this, callDescription.cb)); + } else { + instance.app[callDescription.method].apply(instance.app, [callDescription.endpoint, this.applyHTTPFunction.bind(this, callDescription.cb)]); + } + } + + private applyHTTPFunction(cb: (req: Request, res: Response) => void, req: Request, res: Response) { + this.embark.events.request("authenticator:authorize", req, res, (err: Error) => { + if (err) { + return res.send(err); + } + cb(req, res); + }); + } + + private applyWSFunction(cb: (ws: ws, req: Request) => void, websocket: ws, req: Request) { + this.embark.events.request("authenticator:authorize", websocket, req, (err: Error) => { + if (!err) { + cb(websocket, req); + } + }); + } +} diff --git a/src/lib/modules/authenticator/index.js b/src/lib/modules/authenticator/index.js index 9f3352c64..29f8990d3 100644 --- a/src/lib/modules/authenticator/index.js +++ b/src/lib/modules/authenticator/index.js @@ -81,20 +81,15 @@ class Authenticator { } registerEvents() { - let self = this; - this.events.once('outputDone', () => { this.events.request('authenticator:displayUrl', true); }); this.events.setCommandHandler('authenticator:displayUrl', (firstOutput) => { - const {protocol, port, host, enabled} = this.embark.config.webServerConfig; - - if (enabled) { - if(!firstOutput) this.logger.info(__('Previous token has now been used.')); - this.logger.info(__('Access the web backend with the following url: %s', - (`${protocol}://${host}:${port}/embark?token=${this.authToken}`.underline))); - } + if(!firstOutput) this.logger.info(__('Previous token has now been used.')); + this.events.request('api:url', (apiUrl) => { + this.logger.info(__('Access the web backend with the following url: %s', (`${apiUrl}?token=${this.authToken}`.underline))); + }); }); this.events.setCommandHandler('authenticator:authorize', (req, res, cb) => { @@ -115,7 +110,7 @@ class Authenticator { }); authenticated = (hash === computedHash); } else { - const hash = self.generateRequestHash(req); + const hash = this.generateRequestHash(req); authenticated = (hash === req.headers['x-embark-request-hash']); } diff --git a/src/lib/modules/webserver/index.js b/src/lib/modules/webserver/index.js index 83d914b8f..f59de96cf 100644 --- a/src/lib/modules/webserver/index.js +++ b/src/lib/modules/webserver/index.js @@ -1,21 +1,23 @@ +import {findNextPort} from "../../utils/network"; + const fs = require('../../core/fs.js'); var {canonicalHost} = require('../../utils/host.js'); var utils = require('../../utils/utils.js'); var Server = require('./server.js'); const opn = require('opn'); + require('ejs'); const Templates = { embark_building_placeholder: require('./templates/embark-building-placeholder.html.ejs') }; class WebServer { - constructor(embark, options) { + constructor(embark, _options) { this.embark = embark; this.logger = embark.logger; this.events = embark.events; this.buildDir = embark.config.buildDir; - this.plugins = options.plugins; this.webServerConfig = embark.config.webServerConfig; if (!this.webServerConfig.enabled) { return; @@ -40,7 +42,6 @@ class WebServer { events: this.events, host: this.host, port: this.port, - plugins: this.plugins, openBrowser: this.webServerConfig.openBrowser, protocol: this.webServerConfig.protocol, certOptions : this.webServerConfig.certOptions @@ -72,7 +73,8 @@ class WebServer { }); }); - this.testPort(() => { + findNextPort(this.port).then((newPort) => { + this.server.port = newPort; this.events.request('processes:launch', 'webserver', (_err, message, port) => { this.logger.info(message); this.port = port; @@ -81,23 +83,6 @@ class WebServer { }); } - testPort(done) { - if (this.port === 0) { - this.logger.warn(__('Assigning an available port')); - this.server.port = 0; - return done(); - } - utils.pingEndpoint(this.host, this.port, this.protocol, this.protocol, '', (err) => { - if (err) { // Port is ok - return done(); - } - this.logger.warn(__('Webserver already running on port %s. Assigning an available port', this.port)); - this.port = 0; - this.server.port = 0; - done(); - }); - } - setServiceCheck() { const self = this; diff --git a/src/lib/modules/webserver/server.js b/src/lib/modules/webserver/server.js index 2e6c4ada1..1957a82a3 100644 --- a/src/lib/modules/webserver/server.js +++ b/src/lib/modules/webserver/server.js @@ -5,10 +5,7 @@ const expressWebSocket = require('express-ws'); const express = require('express'); const fs = require('../../core/fs'); const https = require('https'); -var cors = require('cors'); let path = require('path'); -var bodyParser = require('body-parser'); -const helmet = require('helmet'); class Server { constructor(options) { @@ -22,7 +19,6 @@ class Server { this.opened = false; this.openBrowser = options.openBrowser; this.logging = false; - this.plugins = options.plugins; this.enableCatchAll = options.enableCatchAll; this.protocol = options.protocol || 'http'; @@ -71,36 +67,11 @@ class Server { next(); }); - this.app.use(helmet.noCache()); - this.app.use(cors()); this.app.use(main); this.app.use('/coverage', coverage); this.app.use(coverageStyle); this.app.use(express.static(path.join(fs.dappPath(this.dist)), {'index': ['index.html', 'index.htm']})); - this.app.use('/embark', express.static(path.join(__dirname, '../../../../embark-ui/build'))); - - this.app.use(bodyParser.json()); // support json encoded bodies - this.app.use(bodyParser.urlencoded({extended: true})); // support encoded bodies - - this.app.ws('/logs', function(ws, _req) { - self.events.on("log", function(logLevel, logMsg) { - ws.send(JSON.stringify({msg: logMsg, msg_clear: logMsg.stripColors, logLevel: logLevel}), () => {}); - }); - }); - - if (self.plugins) { - let apiCalls = self.plugins.getPluginsProperty("apiCalls", "apiCalls"); - this.app.get('/embark-api/plugins', function(req, res) { - res.send(JSON.stringify(self.plugins.plugins.map((plugin) => { - return {name: plugin.name}; - }))); - }); - - for (let apiCall of apiCalls) { - this.app[apiCall.method].apply(this.app, [apiCall.endpoint, this.applyAPIFunction.bind(this, apiCall.cb)]); - } - } this.app.ws('/', () => {}); const wss = expressWs.getWss('/'); @@ -117,15 +88,6 @@ class Server { }); }); - this.events.on('plugins:register:api', (apiCall) => { - self.app[apiCall.method].apply(self.app, [apiCall.endpoint, this.applyAPIFunction.bind(this, apiCall.cb)]); - }); - - this.app.get('/embark/*', function(req, res) { - self.logger.trace('webserver> GET ' + req.path); - res.sendFile(path.join(__dirname, '../../../../embark-ui/build', 'index.html')); - }); - if (this.enableCatchAll === true) { this.app.get('/*', function(req, res) { self.logger.trace('webserver> GET ' + req.path); @@ -176,16 +138,6 @@ class Server { (this.protocol + '://' + canonicalHost(this.hostname) + ':' + this.port).bold.underline.green; } - applyAPIFunction(cb, req, res) { - this.events.request('authenticator:authorize', req, res, (err) => { - if (err) { - const send = res.send ? res.send.bind(res) : req.send.bind(req); // WS only has the first params - return send(err); - } - cb(req, res); - }); - } - stop(callback) { callback = callback || function () {}; if (!this.server || !this.server.listening) { diff --git a/src/lib/utils/network.ts b/src/lib/utils/network.ts new file mode 100644 index 000000000..ccd0e0c83 --- /dev/null +++ b/src/lib/utils/network.ts @@ -0,0 +1,10 @@ +import * as net from "net"; + +export function findNextPort(port: number) { + const server = net.createServer(); + return new Promise((resolve) => { + server.once("close", () => resolve(port)); + server.on("error", () => resolve(findNextPort(port + 1))); + server.listen(port, () => server.close()); + }); +} diff --git a/src/typings/embark.d.ts b/src/typings/embark.d.ts index 9526f9cd5..3c8042304 100644 --- a/src/typings/embark.d.ts +++ b/src/typings/embark.d.ts @@ -8,6 +8,15 @@ export interface Events { setCommandHandler(name: string, callback: (options: any, cb: () => void) => void): void; } +export interface Plugins { + getPluginsProperty(pluginType: string, property: string, sub_property?: string): any[]; + plugins: Plugin[]; +} + +export interface Plugin { + name: string; +} + export interface Embark { events: Events; registerAPICall: any; diff --git a/yarn.lock b/yarn.lock index 72c38a548..174f53d1b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -838,6 +838,28 @@ dependencies: "@types/node" "*" +"@types/body-parser@*", "@types/body-parser@1.17.0": + version "1.17.0" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.17.0.tgz#9f5c9d9bd04bb54be32d5eb9fc0d8c974e6cf58c" + integrity sha512-a2+YeUjPkztKJu5aIF2yArYFQQp8d51wZ7DavSHjFuY1mqVgidGyzEQ41JIVNy82fXj8yPgy2vJmfIywgESW6w== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/connect@*": + version "3.4.32" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.32.tgz#aa0e9616b9435ccad02bc52b5b454ffc2c70ba28" + integrity sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg== + dependencies: + "@types/node" "*" + +"@types/cors@2.8.4": + version "2.8.4" + resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.4.tgz#50991a759a29c0b89492751008c6af7a7c8267b0" + integrity sha512-ipZjBVsm2tF/n8qFGOuGBkUij9X9ZswVi9G3bx/6dz7POpVa6gVHcj1wsX/LVEn9MMF41fxK/PnZPPoTD1UFPw== + dependencies: + "@types/express" "*" + "@types/debug@^0.0.30": version "0.0.30" resolved "https://registry.yarnpkg.com/@types/debug/-/debug-0.0.30.tgz#dc1e40f7af3b9c815013a7860e6252f6352a84df" @@ -848,6 +870,33 @@ resolved "https://registry.yarnpkg.com/@types/events/-/events-1.2.0.tgz#81a6731ce4df43619e5c8c945383b3e62a89ea86" integrity sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA== +"@types/express-serve-static-core@*": + version "4.16.0" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.16.0.tgz#fdfe777594ddc1fe8eb8eccce52e261b496e43e7" + integrity sha512-lTeoCu5NxJU4OD9moCgm0ESZzweAx0YqsAcab6OB0EB3+As1OaHtKnaGJvcngQxYsi9UNv0abn4/DRavrRxt4w== + dependencies: + "@types/events" "*" + "@types/node" "*" + "@types/range-parser" "*" + +"@types/express-ws@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/express-ws/-/express-ws-3.0.0.tgz#89674edba2e9141916fc4d4d30fbd4f810e6b80b" + integrity sha512-GxsWec7Vp6h7sJuK0PwnZHeXNZnOwQn8kHAbCfvii66it5jXHTWzSg5cgHVtESwJfBLOe9SJ5wmM7C6gsDoyQw== + dependencies: + "@types/express" "*" + "@types/express-serve-static-core" "*" + "@types/ws" "*" + +"@types/express@*", "@types/express@4.16.0": + version "4.16.0" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.16.0.tgz#6d8bc42ccaa6f35cf29a2b7c3333cb47b5a32a19" + integrity sha512-TtPEYumsmSTtTetAPXlJVf3kEqb6wZK0bZojpJQrnD/djV4q1oB6QQ8aKvKqwNPACoe02GNiy5zDzcYivR5Z2w== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "*" + "@types/serve-static" "*" + "@types/fs-extra@^5.0.2": version "5.0.4" resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-5.0.4.tgz#b971134d162cc0497d221adde3dbb67502225599" @@ -877,6 +926,13 @@ resolved "https://registry.yarnpkg.com/@types/handlebars/-/handlebars-4.0.39.tgz#961fb54db68030890942e6aeffe9f93a957807bd" integrity sha512-vjaS7Q0dVqFp85QhyPSZqDKnTTCemcSHNHFvDdalO1s0Ifz5KuE64jQD5xoUkfdWwF4WpqdJEl7LsWH8rzhKJA== +"@types/helmet@0.0.42": + version "0.0.42" + resolved "https://registry.yarnpkg.com/@types/helmet/-/helmet-0.0.42.tgz#845954fb171000c9b9caa367febf6769bd589eaa" + integrity sha512-xQjlolRfr7LVa55sGnjtJGcV6/Hf7cfD1IuZo1Do+o3UobOoJJSLIbwkhd9tW18F1Kp4uB77T8TsJ2XKUDwp7g== + dependencies: + "@types/express" "*" + "@types/i18n@0.8.3": version "0.8.3" resolved "https://registry.yarnpkg.com/@types/i18n/-/i18n-0.8.3.tgz#f602164f2fae486ea87590f6be5d6dd5db1664e6" @@ -887,6 +943,11 @@ resolved "https://registry.yarnpkg.com/@types/lockfile/-/lockfile-1.0.0.tgz#76a7c19c50fe8ee2b1666d653ff5d557c30fe0ff" integrity sha512-pD6JuijPmrfi84qF3/TzGQ7zi0QIX+d7ZdetD6jUA6cp+IsCzAquXZfi5viesew+pfpOTIdAVKuh1SHA7KeKzg== +"@types/mime@*": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.0.tgz#5a7306e367c539b9f6543499de8dd519fac37a8b" + integrity sha512-A2TAGbTFdBw9azHbpVd+/FkdW2T6msN1uct1O9bH3vTerEHKZhTXJUQXy+hNq1B0RagfU8U+KBdqiZpxjhOUQA== + "@types/minimatch@*": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" @@ -919,11 +980,24 @@ resolved "https://registry.yarnpkg.com/@types/pretty-ms/-/pretty-ms-3.2.0.tgz#cdd35f7edac2310bbe2af86f5244625db7a101b1" integrity sha512-jF8PYR5Nm2w148Icj+Xf4CcVO+YrrpVyRSZPmSG67W2H3WrAAET7jP22txHpk6rnwPGJ8asW7syiyQTPcWWPAQ== +"@types/range-parser@*": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c" + integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA== + "@types/semver@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-5.5.0.tgz#146c2a29ee7d3bae4bf2fcb274636e264c813c45" integrity sha512-41qEJgBH/TWgo5NFSvBCJ1qkoi3Q6ONSF2avrHq1LVEZfYpdHmj0y9SuTK+u9ZhG1sYQKBL1AWXKyLWP4RaUoQ== +"@types/serve-static@*": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.2.tgz#f5ac4d7a6420a99a6a45af4719f4dcd8cd907a48" + integrity sha512-/BZ4QRLpH/bNYgZgwhKEh+5AsboDBcUdlBYgzoLX0fpj3Y2gp6EApyOlM3bK53wQS/OE1SrdSYBAbux2D1528Q== + dependencies: + "@types/express-serve-static-core" "*" + "@types/mime" "*" + "@types/tar@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/tar/-/tar-4.0.0.tgz#e3239d969eeb693a012200613860d0eb871c94f0" @@ -949,6 +1023,14 @@ "@types/bn.js" "*" "@types/underscore" "*" +"@types/ws@*": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-6.0.1.tgz#ca7a3f3756aa12f62a0a62145ed14c6db25d5a28" + integrity sha512-EzH8k1gyZ4xih/MaZTXwT2xOkPiIMSrhQ9b8wrlX88L0T02eYsddatQlwVFlEPyEqV0ChpdpNnE51QPH6NVT4Q== + dependencies: + "@types/events" "*" + "@types/node" "*" + "@webassemblyjs/ast@1.7.6": version "1.7.6" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.7.6.tgz#3ef8c45b3e5e943a153a05281317474fef63e21e"