Skip to content
This repository has been archived by the owner on Jul 22, 2020. It is now read-only.

Commit

Permalink
fix: make EndpointConfig dynamic and add v1 App UI to select the cluster
Browse files Browse the repository at this point in the history
  • Loading branch information
mvines committed Jul 6, 2019
1 parent 373ec1f commit c4a36b9
Show file tree
Hide file tree
Showing 7 changed files with 265 additions and 82 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"google-map-react": "^1.1.4",
"humps": "^2.0.1",
"ip": "^1.1.5",
"localforage": "^1.7.3",
"lodash": "^4.17.11",
"mobx": "^5.11.0",
"mobx-react-lite": "^1.4.1",
Expand Down
127 changes: 62 additions & 65 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {createBrowserHistory} from 'history';
import {CssBaseline} from '@material-ui/core';

import {sleep} from './sleep';
import EndpointConfig from './EndpointConfig';
import * as EndpointConfig from './EndpointConfig';
// v1 components
import BxDataTable from './v1/BxDataTable';
import BxTransactionChart from './v1/BxTransactionChart';
Expand All @@ -33,8 +33,6 @@ import {stylesV2, themeV2} from './v2/ThemeV2';

const history = createBrowserHistory();

const BLOCK_EXPLORER_API_BASE = EndpointConfig.BLOCK_EXPLORER_API_BASE;

const BxAppBarThemed = withStyles(stylesV1)(BxAppBar);
const BxDialogThemed = withStyles(stylesV1)(BxDialog);
const BxDialogTransactionsThemed = withStyles(stylesV1)(BxDialogTransactions);
Expand All @@ -58,14 +56,7 @@ class App extends Component {
constructor(props) {
super(props);

this.ws = null;

// unused (for now)
// this.connection = new Connection(EndpointConfig.BLOCK_EXPLORER_RPC_URL);

this.state = {
enabled: true,
dialogOpen: false,
this.defaultState = {
selectedValue: null,
currentMatch: null,
stateLoading: false,
Expand All @@ -82,35 +73,20 @@ class App extends Component {
transactions: [],
blocks: [],
};

const self = this;

// update global info (once, on load)
this.getRemoteState(
'globalStats',
`http:${BLOCK_EXPLORER_API_BASE}/global-stats`,
);

// update cluster info (once, on load)
this.getRemoteState(
'clusterInfo',
`http:${BLOCK_EXPLORER_API_BASE}/cluster-info`,
null,
null,
self.parseClusterInfo,
this.state = Object.assign(
{
enabled: true,
dialogOpen: false,
ws: null,
},
this.defaultState,
);

// update blocks (once, on load)
self.updateBlocks();

self.updateTxnStats();
setInterval(() => {
self.updateTxnStats();
this.updateTxnStats();
}, 30000);

self.updateTransactions();
setInterval(() => {
self.updateTransactions();
this.updateTransactions();
}, 10000);
}

Expand Down Expand Up @@ -175,10 +151,7 @@ class App extends Component {
}

updateTxnStats() {
this.getRemoteState(
'txnStats',
`http:${BLOCK_EXPLORER_API_BASE}/txn-stats`,
);
this.getRemoteState('txnStats', `${EndpointConfig.getApiUrl()}txn-stats`);
}

updateBlocks() {
Expand All @@ -202,7 +175,7 @@ class App extends Component {

this.getRemoteState(
'blocks',
`http:${BLOCK_EXPLORER_API_BASE}/blk-timeline`,
`${EndpointConfig.getApiUrl()}blk-timeline`,
blkFun,
10,
);
Expand All @@ -221,7 +194,7 @@ class App extends Component {

this.getRemoteState(
'transactions',
`http:${BLOCK_EXPLORER_API_BASE}/txn-timeline`,
`${EndpointConfig.getApiUrl()}txn-timeline`,
txnFun,
10,
);
Expand Down Expand Up @@ -276,40 +249,57 @@ class App extends Component {
}
};

componentDidMount() {
const self = this;

if (!self.ws) {
let ws = new RobustWebSocket(`ws:${BLOCK_EXPLORER_API_BASE}/`);
onEndpointChange() {
if (this.ws) {
this.ws.close();
this.ws = null;
}

ws.addEventListener('open', function() {
ws.send(JSON.stringify({hello: 'world'}));
});
this.getRemoteState(
'globalStats',
`${EndpointConfig.getApiUrl()}global-stats`,
);
this.getRemoteState(
'clusterInfo',
`${EndpointConfig.getApiUrl()}cluster-info`,
null,
null,
this.parseClusterInfo,
);
this.updateBlocks();
this.updateTxnStats();
this.updateTransactions();

ws.addEventListener('message', function(event) {
if (!self.state.enabled) {
return;
}
const ws = new RobustWebSocket(EndpointConfig.getApiWebsocketUrl());
ws.addEventListener('open', () => {
ws.send(JSON.stringify({hello: 'world'}));
});

self.onMessage(JSON.parse(event.data));
});
ws.addEventListener('message', event => {
if (this.state.enabled) {
this.onMessage(JSON.parse(event.data));
}
});

self.ws = ws;
}
this.ws = ws;
}

if (!self.locationListener) {
let locationListener = this.handleLocationChange();
componentDidMount() {
this.onEndpointChange();

if (!this.locationListener) {
const locationListener = this.handleLocationChange();
history.listen(locationListener);
locationListener(window.location);

self.locationListener = locationListener;
this.locationListener = locationListener;
}
}

componentWillUnmount() {
if (this.ws) {
this.ws.close();
this.ws = null;
}
}

Expand Down Expand Up @@ -462,6 +452,12 @@ class App extends Component {
});
};

setEndpointName = event => {
EndpointConfig.setEndpointName(event.target.value);
this.onEndpointChange();
this.updateStateAttributes(this.defaultState);
};

handleSearch = () => event => {
let value = event.target.value;
event.target.value = '';
Expand All @@ -470,7 +466,7 @@ class App extends Component {
return;
}

let url = `${BLOCK_EXPLORER_API_BASE}/search/${value}`;
let url = `${EndpointConfig.getApiUrl()}search/${value}`;

axios.get(url).then(response => {
let result = response.data;
Expand All @@ -485,19 +481,19 @@ class App extends Component {
let url = null;

if (type === 'txns-by-prgid') {
url = `${BLOCK_EXPLORER_API_BASE}/txns-by-prgid/${id}`;
url = `${EndpointConfig.getApiUrl()}txns-by-prgid/${id}`;
}

if (type === 'txn') {
url = `${BLOCK_EXPLORER_API_BASE}/txn/${id}`;
url = `${EndpointConfig.getApiUrl()}txn/${id}`;
}

if (type === 'ent') {
url = `${BLOCK_EXPLORER_API_BASE}/ent/${id}`;
url = `${EndpointConfig.getApiUrl()}ent/${id}`;
}

if (type === 'blk') {
url = `${BLOCK_EXPLORER_API_BASE}/blk/${id}`;
url = `${EndpointConfig.getApiUrl()}blk/${id}`;
}

return url;
Expand Down Expand Up @@ -573,6 +569,7 @@ class App extends Component {
handleSearch={self.handleSearch(self)}
enabled={this.state.enabled}
handleSwitch={this.toggleEnabled(self)}
handleSetEndpointName={this.setEndpointName}
handleMap={this.showMap(self)}
/>
<div>
Expand Down
99 changes: 91 additions & 8 deletions src/EndpointConfig.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,100 @@
import localforage from 'localforage';
import {parse as urlParse, format as urlFormat} from 'url';

const urlMap = {
local: `http://${window.location.hostname}:8899`,
'testnet-edge': 'http://edge.testnet.solana.com:8899',
'testnet-beta': 'http://beta.testnet.solana.com:8899',
testnet: 'http://testnet.solana.com:8899',
/*
TODO: Switch to TLS endpoints...
'testnet-edge': 'https://api.edge.testnet.solana.com',
'testnet-beta': 'https://api.beta.testnet.solana.com',
testnet: 'https://api.testnet.solana.com',
*/
};

let endpointName = process.env.NODE_ENV === 'development' ? 'local' : 'testnet';

export async function load() {
try {
const newEndpointName = await localforage.getItem('endpointName');
if (typeof urlMap[newEndpointName] === 'string') {
endpointName = newEndpointName;
}
} catch (err) {
console.log(
`Unable to load endpointName from localforage, using default of ${endpointName}: ${err}`,
);
}
console.log('EndpointConfig loaded. endpointName:', endpointName);
}

export function getEndpointName() {
return endpointName;
}

export function getEndpoints() {
return Object.keys(urlMap);
}

export function setEndpointName(newEndpointName) {
if (typeof urlMap[newEndpointName] !== 'string') {
throw new Error(`Unknown endpoint: ${newEndpointName}`);
}
endpointName = newEndpointName;
console.log('endpointName is now', endpointName);
localforage.setItem('endpointName', endpointName).catch(err => {
console.log(`Failed to set endpointName in localforage: ${err}`);
});
}

export function getRpcUrl() {
return urlMap[endpointName];
}

export function getApiUrl() {
const urlParts = urlParse(getRpcUrl());
urlParts.host = '';
if (urlParts.protocol === 'https:') {
urlParts.port = '3443';
} else {
urlParts.port = '3001';
}
const url = urlFormat(urlParts);
console.info('getApiUrl:', url);
return url;
}

export function getApiWebsocketUrl() {
const urlParts = urlParse(getApiUrl());
urlParts.host = '';
if (urlParts.protocol === 'https:') {
urlParts.protocol = 'wss:';
urlParts.port = '3444';
} else {
urlParts.protocol = 'ws:';
}
const url = urlFormat(urlParts);
console.info('getApiWebsocketUrl:', url);
return url;
}

export function getMetricsDashboardUrl() {
const matches = window.location.hostname.match('(.*).testnet.solana.com');
let matches;
if (endpointName === 'local') {
matches = window.location.hostname.match('([^.]*).testnet.solana.com');
} else {
const endpointUrl = urlMap[endpointName];
matches = endpointUrl.match('([^.]*).testnet.solana.com');
}

let url =
'https://metrics.solana.com:3000/d/testnet-beta/testnet-monitor-beta?refresh=5s&from=now-5m&to=now';
if (matches) {
const testnet = matches[1];
url += `&var-testnet=testnet-${testnet}`;
}
console.log('getMetricsDashboardUrl:', url);
return url;
}

let EndpointConfig = {
BLOCK_EXPLORER_API_BASE: `//${window.location.hostname}:3001`,
BLOCK_EXPLORER_RPC_URL: `http://${window.location.hostname}:8899`,
};

export default EndpointConfig;
17 changes: 11 additions & 6 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,18 @@ import './index.css';
import App from './App';
import AppV2 from './AppV2';
import * as serviceWorker from './serviceWorker';
import * as EndpointConfig from './EndpointConfig';

ReactDOM.render(
<BrowserRouter>
{window.location.pathname.includes('rc') ? <AppV2 /> : <App />}
</BrowserRouter>,
document.getElementById('root'),
);
async function main() {
await EndpointConfig.load();
ReactDOM.render(
<BrowserRouter>
{window.location.pathname.includes('rc') ? <AppV2 /> : <App />}
</BrowserRouter>,
document.getElementById('root'),
);
}
main();

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
Expand Down
Loading

0 comments on commit c4a36b9

Please sign in to comment.