UI server refactoring (#5580)
* Full API in Authenticated WS server. * Replacing UI server with Hyper. * Solving CLI, RPCs and tests. * Porting signer tests. * Fixing origin recognition for dapps/rpc. * Fixing tests. Adding parity-rpc-client to test. * Dapps exposed as RPC method. * JS code to support new connection scheme. * Fixing dapps tests. * Updating allowed origins/hosts to support web3.site. * Fixing tests, fixing UI. * Fixing tests. * Removing invalid tests. * Fixing merge. * 404 fallback for UI * Improve ContentFetcher constructor readability. * Naming. * Update .gitlab-ci.yml fix CI lint error * Fixing tests and linting issues. * Fixing new tests. * UI hosts. * Submodules fix.
This commit is contained in:
committed by
Arkadiy Paronyan
parent
7499efecf6
commit
cbcc369a2d
@@ -90,15 +90,14 @@ export default class Parity {
|
||||
.execute('parity_consensusCapability');
|
||||
}
|
||||
|
||||
dappsPort () {
|
||||
dappsList () {
|
||||
return this._transport
|
||||
.execute('parity_dappsPort')
|
||||
.then(outNumber);
|
||||
.execute('parity_dappsList');
|
||||
}
|
||||
|
||||
dappsInterface () {
|
||||
dappsUrl () {
|
||||
return this._transport
|
||||
.execute('parity_dappsInterface');
|
||||
.execute('parity_dappsUrl');
|
||||
}
|
||||
|
||||
decryptMessage (address, data) {
|
||||
@@ -530,12 +529,6 @@ export default class Parity {
|
||||
.execute('parity_setVaultMeta', vaultName, JSON.stringify(meta));
|
||||
}
|
||||
|
||||
signerPort () {
|
||||
return this._transport
|
||||
.execute('parity_signerPort')
|
||||
.then(outNumber);
|
||||
}
|
||||
|
||||
signMessage (address, password, messageHash) {
|
||||
return this._transport
|
||||
.execute('parity_signMessage', inAddress(address), password, inHex(messageHash));
|
||||
@@ -567,4 +560,9 @@ export default class Parity {
|
||||
return this._transport
|
||||
.execute('parity_versionInfo');
|
||||
}
|
||||
|
||||
wsUrl () {
|
||||
return this._transport
|
||||
.execute('parity_wsUrl');
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -16,8 +16,6 @@
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { parityNode } from '../../../environment';
|
||||
|
||||
const styles = {
|
||||
padding: '.5em',
|
||||
border: '1px solid #777'
|
||||
@@ -34,7 +32,7 @@ export default (address) => {
|
||||
|
||||
return (
|
||||
<img
|
||||
src={ `${parityNode}/api/content/${address.replace(/^0x/, '')}` }
|
||||
src={ `/api/content/${address.replace(/^0x/, '')}` }
|
||||
alt={ address }
|
||||
style={ styles }
|
||||
/>
|
||||
|
||||
@@ -30,7 +30,6 @@ import styles from './token.css';
|
||||
import { metaDataKeys } from '../../constants';
|
||||
|
||||
import { api } from '../../parity';
|
||||
import { parityNode } from '../../../../environment';
|
||||
|
||||
export default class Token extends Component {
|
||||
static propTypes = {
|
||||
@@ -312,7 +311,7 @@ export default class Token extends Component {
|
||||
</span> meta-data:
|
||||
</p>
|
||||
<div className={ styles['meta-image'] }>
|
||||
<img src={ `${parityNode}/api/content/${imageHash}/` } />
|
||||
<img src={ `/api/content/${imageHash}/` } />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -55,6 +55,9 @@ class FakeTransport {
|
||||
return Promise.reject('not connected');
|
||||
}
|
||||
|
||||
addMiddleware () {
|
||||
}
|
||||
|
||||
on () {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,14 +19,4 @@
|
||||
|
||||
import './tests';
|
||||
|
||||
const parityNode = (
|
||||
process.env.PARITY_URL && `http://${process.env.PARITY_URL}`
|
||||
) || (
|
||||
process.env.NODE_ENV === 'production'
|
||||
? 'http://127.0.0.1:8545'
|
||||
: ''
|
||||
);
|
||||
|
||||
export {
|
||||
parityNode
|
||||
};
|
||||
export {};
|
||||
|
||||
@@ -53,8 +53,7 @@ if (process.env.NODE_ENV === 'development') {
|
||||
}
|
||||
|
||||
const AUTH_HASH = '#/auth?';
|
||||
const parityUrl = process.env.PARITY_URL || window.location.host;
|
||||
const urlScheme = window.location.href.match(/^https/) ? 'wss://' : 'ws://';
|
||||
const parityUrl = process.env.PARITY_URL || '127.0.0.1:8546';
|
||||
|
||||
let token = null;
|
||||
|
||||
@@ -62,7 +61,7 @@ if (window.location.hash && window.location.hash.indexOf(AUTH_HASH) === 0) {
|
||||
token = qs.parse(window.location.hash.substr(AUTH_HASH.length)).token;
|
||||
}
|
||||
|
||||
const api = new SecureApi(`${urlScheme}${parityUrl}`, token);
|
||||
const api = new SecureApi(parityUrl, token);
|
||||
|
||||
patchApi(api);
|
||||
loadSender(api);
|
||||
|
||||
@@ -143,25 +143,34 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
dappsPort: {
|
||||
section: SECTION_NODE,
|
||||
desc: 'Returns the port the dapps are running on, error if not enabled.',
|
||||
dappsList: {
|
||||
subdoc: SUBDOC_SET,
|
||||
desc: 'Returns a list of available local dapps.',
|
||||
params: [],
|
||||
returns: {
|
||||
type: Quantity,
|
||||
desc: 'The port number',
|
||||
example: 8080
|
||||
type: Array,
|
||||
desc: 'The list of dapps',
|
||||
example: [
|
||||
{
|
||||
author: 'Parity Technologies Ltd',
|
||||
description: 'A skeleton dapp',
|
||||
iconUrl: 'title.png',
|
||||
id: 'skeleton',
|
||||
name: 'Skeleton',
|
||||
version: '0.1'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
dappsInterface: {
|
||||
dappsUrl: {
|
||||
section: SECTION_NODE,
|
||||
desc: 'Returns the interface the dapps are running on, error if not enabled.',
|
||||
desc: 'Returns the hostname and the port of dapps/rpc server, error if not enabled.',
|
||||
params: [],
|
||||
returns: {
|
||||
type: String,
|
||||
desc: 'The interface',
|
||||
example: '127.0.0.1'
|
||||
desc: 'The hostname and port number',
|
||||
example: 'localhost:8545'
|
||||
}
|
||||
},
|
||||
|
||||
@@ -788,17 +797,6 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
signerPort: {
|
||||
section: SECTION_NODE,
|
||||
desc: 'Returns the port the signer is running on, error if not enabled',
|
||||
params: [],
|
||||
returns: {
|
||||
type: Quantity,
|
||||
desc: 'The port number',
|
||||
example: 8180
|
||||
}
|
||||
},
|
||||
|
||||
transactionsLimit: {
|
||||
section: SECTION_MINING,
|
||||
desc: 'Changes limit for transactions in queue.',
|
||||
@@ -1916,6 +1914,17 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
wsUrl: {
|
||||
section: SECTION_NODE,
|
||||
desc: 'Returns the hostname and the port of WebSockets/Signer server, error if not enabled.',
|
||||
params: [],
|
||||
returns: {
|
||||
type: String,
|
||||
desc: 'The hostname and port number',
|
||||
example: 'localhost:8546'
|
||||
}
|
||||
},
|
||||
|
||||
composeTransaction: {
|
||||
desc: 'Given partial transaction request produces transaction with all fields filled in. Such transaction can be then signed externally.',
|
||||
params: [
|
||||
@@ -1997,4 +2006,5 @@ export default {
|
||||
example: 'QmSbFjqjd6nFwNHqsBCC7SK8GShGcayLUEtysJjNGhZAnC'
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -27,21 +27,28 @@ export default class SecureApi extends Api {
|
||||
_needsToken = false;
|
||||
_tokens = [];
|
||||
|
||||
_dappsInterface = null;
|
||||
_dappsPort = 8545;
|
||||
_signerPort = 8180;
|
||||
_dappsUrl = null;
|
||||
_wsUrl = null;
|
||||
|
||||
static getTransport (url, sysuiToken) {
|
||||
return new Api.Transport.Ws(url, sysuiToken, false);
|
||||
static getTransport (url, sysuiToken, protocol) {
|
||||
const proto = protocol() === 'https:' ? 'wss:' : 'ws:';
|
||||
|
||||
return new Api.Transport.Ws(`${proto}//${url}`, sysuiToken, false);
|
||||
}
|
||||
|
||||
constructor (url, nextToken, getTransport = SecureApi.getTransport) {
|
||||
// Returns a protocol with `:` at the end.
|
||||
static protocol () {
|
||||
return window.location.protocol;
|
||||
}
|
||||
|
||||
constructor (url, nextToken, getTransport = SecureApi.getTransport, protocol = SecureApi.protocol) {
|
||||
const sysuiToken = store.get('sysuiToken');
|
||||
const transport = getTransport(url, sysuiToken);
|
||||
const transport = getTransport(url, sysuiToken, protocol);
|
||||
|
||||
super(transport);
|
||||
|
||||
this._url = url;
|
||||
this._wsUrl = url;
|
||||
this.protocol = protocol;
|
||||
// Try tokens from localStorage, from hash and 'initial'
|
||||
this._tokens = uniq([sysuiToken, nextToken, 'initial'])
|
||||
.filter((token) => token)
|
||||
@@ -53,12 +60,30 @@ export default class SecureApi extends Api {
|
||||
this.connect();
|
||||
}
|
||||
|
||||
get _dappsAddress () {
|
||||
if (!this._dappsUrl) {
|
||||
return {
|
||||
host: null,
|
||||
port: 8545
|
||||
};
|
||||
}
|
||||
|
||||
const [host, port] = this._dappsUrl.split(':');
|
||||
|
||||
return {
|
||||
host,
|
||||
port: parseInt(port, 10)
|
||||
};
|
||||
}
|
||||
|
||||
get dappsPort () {
|
||||
return this._dappsPort;
|
||||
return this._dappsAddress.port;
|
||||
}
|
||||
|
||||
get dappsUrl () {
|
||||
return `http://${this.hostname}:${this.dappsPort}`;
|
||||
const { port } = this._dappsAddress;
|
||||
|
||||
return `${this.protocol()}//${this.hostname}:${port}`;
|
||||
}
|
||||
|
||||
get hostname () {
|
||||
@@ -66,15 +91,13 @@ export default class SecureApi extends Api {
|
||||
return 'dapps.parity';
|
||||
}
|
||||
|
||||
if (!this._dappsInterface || this._dappsInterface === '0.0.0.0') {
|
||||
const { host } = this._dappsAddress;
|
||||
|
||||
if (!host || host === '0.0.0.0') {
|
||||
return window.location.hostname;
|
||||
}
|
||||
|
||||
return this._dappsInterface;
|
||||
}
|
||||
|
||||
get signerPort () {
|
||||
return this._signerPort;
|
||||
return host;
|
||||
}
|
||||
|
||||
get isConnecting () {
|
||||
@@ -98,18 +121,18 @@ export default class SecureApi extends Api {
|
||||
* (`signerPort`, `dappsInterface`, `dappsPort`, ...)
|
||||
*/
|
||||
configure (configuration) {
|
||||
const { dappsInterface, dappsPort, signerPort } = configuration;
|
||||
const { dappsInterface, dappsPort, signerPort, wsPort } = configuration;
|
||||
|
||||
if (dappsInterface) {
|
||||
this._dappsInterface = dappsInterface;
|
||||
this._dappsUrl = `${dappsInterface}:${this._dappsAddress.port}`;
|
||||
}
|
||||
|
||||
if (dappsPort) {
|
||||
this._dappsPort = dappsPort;
|
||||
this._dappsUrl = `${this.hostname}:${dappsPort}`;
|
||||
}
|
||||
|
||||
if (signerPort) {
|
||||
this._signerPort = signerPort;
|
||||
if (signerPort || wsPort) {
|
||||
this._wsUrl = `${this.hostname}:${signerPort || wsPort}`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,9 +189,7 @@ export default class SecureApi extends Api {
|
||||
* otherwise (HEAD request to the Node)
|
||||
*/
|
||||
isNodeUp () {
|
||||
const url = this._url.replace(/wss?/, 'http');
|
||||
|
||||
return fetch(url, { method: 'HEAD' })
|
||||
return fetch(`${this.protocol()}//${this._wsUrl}`, { method: 'HEAD', mode: 'no-cors' })
|
||||
.then(
|
||||
(r) => r.status === 200,
|
||||
() => false
|
||||
@@ -297,14 +318,12 @@ export default class SecureApi extends Api {
|
||||
_fetchSettings () {
|
||||
return Promise
|
||||
.all([
|
||||
this.parity.dappsPort(),
|
||||
this.parity.dappsInterface(),
|
||||
this.parity.signerPort()
|
||||
this.parity.dappsUrl(),
|
||||
this.parity.wsUrl()
|
||||
])
|
||||
.then(([dappsPort, dappsInterface, signerPort]) => {
|
||||
this._dappsPort = dappsPort.toNumber();
|
||||
this._dappsInterface = dappsInterface;
|
||||
this._signerPort = signerPort.toNumber();
|
||||
.then(([dappsUrl, wsUrl]) => {
|
||||
this._dappsUrl = dappsUrl;
|
||||
this._wsUrl = dappsUrl;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -25,21 +25,6 @@ import builtinJson from '~/views/Dapps/builtin.json';
|
||||
|
||||
const builtinApps = builtinJson.filter((app) => app.id);
|
||||
|
||||
function getHost (api) {
|
||||
const host = process.env.DAPPS_URL ||
|
||||
(
|
||||
process.env.NODE_ENV === 'production'
|
||||
? api.dappsUrl
|
||||
: ''
|
||||
);
|
||||
|
||||
if (host === '/') {
|
||||
return '';
|
||||
}
|
||||
|
||||
return host;
|
||||
}
|
||||
|
||||
export function subscribeToChanges (api, dappReg, callback) {
|
||||
return dappReg
|
||||
.getContract()
|
||||
@@ -105,12 +90,7 @@ export function fetchBuiltinApps () {
|
||||
}
|
||||
|
||||
export function fetchLocalApps (api) {
|
||||
return fetch(`${getHost(api)}/api/apps`)
|
||||
.then((response) => {
|
||||
return response.ok
|
||||
? response.json()
|
||||
: [];
|
||||
})
|
||||
return api.parity.dappsList()
|
||||
.then((apps) => {
|
||||
return apps
|
||||
.map((app) => {
|
||||
@@ -195,7 +175,7 @@ export function fetchManifest (api, manifestHash) {
|
||||
}
|
||||
|
||||
return fetch(
|
||||
`${getHost(api)}/api/content/${manifestHash}/`,
|
||||
`/api/content/${manifestHash}/`,
|
||||
{ redirect: 'follow', mode: 'cors' }
|
||||
)
|
||||
.then((response) => {
|
||||
|
||||
@@ -26,17 +26,11 @@ const APPID_DAPPREG = '0x7bbc4f1a27628781b96213e781a1b8eec6982c1db8fac739af6e4c5
|
||||
const APPID_GHH = '0x058740ee9a5a3fb9f1cfa10752baec87e09cc45cd7027fd54708271aca300c75';
|
||||
const APPID_LOCALTX = '0xae74ad174b95cdbd01c88ac5b73a296d33e9088fc2a200e76bcedf3a94a7815d';
|
||||
const APPID_TOKENDEPLOY = '0xf9f2d620c2e08f83e45555247146c62185e4ab7cf82a4b9002a265a0d020348f';
|
||||
const FETCH_OK = {
|
||||
ok: true,
|
||||
status: 200
|
||||
};
|
||||
|
||||
let globalContractsGet;
|
||||
let globalFetch;
|
||||
|
||||
function stubGlobals () {
|
||||
globalContractsGet = Contracts.get;
|
||||
globalFetch = global.fetch;
|
||||
|
||||
Contracts.get = () => {
|
||||
return {
|
||||
@@ -50,31 +44,21 @@ function stubGlobals () {
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
global.fetch = (url) => {
|
||||
switch (url) {
|
||||
case '/api/apps':
|
||||
return Promise.resolve(Object.assign({}, FETCH_OK, {
|
||||
json: sinon.stub().resolves([]) // TODO: Local stubs in here
|
||||
}));
|
||||
|
||||
default:
|
||||
console.log('Unknown fetch stub endpoint', url);
|
||||
return Promise.reject();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function restoreGlobals () {
|
||||
Contracts.get = globalContractsGet;
|
||||
global.fetch = globalFetch;
|
||||
}
|
||||
|
||||
let api;
|
||||
let store;
|
||||
|
||||
function create () {
|
||||
api = {};
|
||||
api = {
|
||||
parity: {
|
||||
dappsList: () => Promise.resolve([])
|
||||
}
|
||||
};
|
||||
store = new Store(api);
|
||||
|
||||
return store;
|
||||
|
||||
@@ -34,8 +34,8 @@ let store;
|
||||
|
||||
function createApi () {
|
||||
api = {
|
||||
dappsPort: 8080,
|
||||
dappsUrl: 'http://home.web3.site:8080',
|
||||
dappsPort: 8545,
|
||||
dappsUrl: 'http://home.web3.site:8545',
|
||||
parity: {
|
||||
listRecentDapps: sinon.stub().resolves(TEST_HISTORY)
|
||||
},
|
||||
@@ -159,7 +159,7 @@ describe('views/Web/Store', () => {
|
||||
it('encodes current', () => {
|
||||
store.setCurrentUrl(TEST_URL1);
|
||||
expect(store.encodedPath).to.match(
|
||||
/http:\/\/home\.web3\.site:8080\/web\/DSTPRV1BD1T78W1T5WQQ6VVDCMQ78SBKEGQ68VVDC5MPWBK3DXPG\?t=[0-9]*$/
|
||||
/http:\/\/home\.web3\.site:8545\/web\/DSTPRV1BD1T78W1T5WQQ6VVDCMQ78SBKEGQ68VVDC5MPWBK3DXPG\?t=[0-9]*$/
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -167,7 +167,7 @@ describe('views/Web/Store', () => {
|
||||
it('encodes current', () => {
|
||||
store.setCurrentUrl(TEST_URL1);
|
||||
expect(store.encodedUrl).to.match(
|
||||
/^http:\/\/DSTPRV1BD1T78W1T5WQQ6VVDCMQ78SBKEGQ68VVDC5MPWBK3DXPG\.web\.web3\.site:8080\?t=[0-9]*$/
|
||||
/^http:\/\/DSTPRV1BD1T78W1T5WQQ6VVDCMQ78SBKEGQ68VVDC5MPWBK3DXPG\.web\.web3\.site:8545\?t=[0-9]*$/
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,26 +15,21 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
// test only
|
||||
/**
|
||||
* Run `DAPPS_URL="/" PARITY_URL="127.0.0.1:8180" NODE_ENV="production" npm run build`
|
||||
* Run `DAPPS_URL="/" PARITY_URL="127.0.0.1:8546" NODE_ENV="production" npm run build`
|
||||
* to build the project ; use this server to test that the minifed
|
||||
* version is working (this is a simple proxy server)
|
||||
*/
|
||||
|
||||
var express = require('express');
|
||||
var proxy = require('http-proxy-middleware');
|
||||
|
||||
var Shared = require('./shared');
|
||||
|
||||
var app = express();
|
||||
var wsProxy = proxy('ws://127.0.0.1:8180', { changeOrigin: true });
|
||||
|
||||
Shared.addProxies(app);
|
||||
|
||||
app.use(express.static('.build'));
|
||||
app.use(wsProxy);
|
||||
|
||||
var server = app.listen(process.env.PORT || 3000, function () {
|
||||
console.log('Listening on port', server.address().port);
|
||||
});
|
||||
|
||||
server.on('upgrade', wsProxy.upgrade);
|
||||
|
||||
@@ -22,7 +22,6 @@ const webpackHotMiddleware = require('webpack-hot-middleware');
|
||||
const http = require('http');
|
||||
const express = require('express');
|
||||
const ProgressBar = require('progress');
|
||||
const proxy = require('http-proxy-middleware');
|
||||
|
||||
const webpackConfig = require('./app');
|
||||
const Shared = require('./shared');
|
||||
@@ -84,18 +83,13 @@ app.use(webpackDevMiddleware(compiler, {
|
||||
}
|
||||
}));
|
||||
|
||||
var wsProxy = proxy('ws://127.0.0.1:8180', { changeOrigin: true });
|
||||
|
||||
// Add the dev proxies in the express App
|
||||
Shared.addProxies(app);
|
||||
|
||||
app.use(express.static(webpackConfig.output.path));
|
||||
app.use(wsProxy);
|
||||
|
||||
const server = http.createServer(app);
|
||||
server.listen(process.env.PORT || 3000, function () {
|
||||
console.log('Listening on port', server.address().port);
|
||||
progressBar = new ProgressBar('[:bar] :percent :etas', { total: 50 });
|
||||
});
|
||||
|
||||
server.on('upgrade', wsProxy.upgrade);
|
||||
|
||||
@@ -162,16 +162,8 @@ function getDappsEntry () {
|
||||
function addProxies (app) {
|
||||
const proxy = require('http-proxy-middleware');
|
||||
|
||||
app.use(proxy((pathname, req) => {
|
||||
return pathname === '/' && req.method === 'HEAD';
|
||||
}, {
|
||||
target: 'http://127.0.0.1:8180',
|
||||
changeOrigin: true,
|
||||
autoRewrite: true
|
||||
}));
|
||||
|
||||
app.use('/api', proxy({
|
||||
target: 'http://127.0.0.1:8545',
|
||||
target: 'http://127.0.0.1:8180',
|
||||
changeOrigin: true,
|
||||
autoRewrite: true
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user