Skip to content

Commit

Permalink
migrating from angular to react/flux
Browse files Browse the repository at this point in the history
  • Loading branch information
Phoenixmatrix committed May 7, 2015
1 parent 6bebad9 commit 2d649e1
Show file tree
Hide file tree
Showing 43 changed files with 915 additions and 1,341 deletions.
19 changes: 17 additions & 2 deletions README.md
Expand Up @@ -3,7 +3,7 @@ PhoenixMatrix web development proxy

[![Join the chat at https://gitter.im/Phoenixmatrix/phoenixmatrix-proxy](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Phoenixmatrix/phoenixmatrix-proxy?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)

_v0.2.0 technical preview_
_v0.2.1 technical preview_

Web debugging proxy in the spirit of Fiddler and Charles Proxy, written in JavaScript with [Electron Shell](http://electron.atom.io/)
and node.
Expand All @@ -12,6 +12,9 @@ Tested on MacOSX Yosemite, Windows 8.1 and Ubuntu 14.

## Release notes

**v0.2.0**
* Migrated from Angular to React

**v0.2.0**
* Moved from nw.js to Electron Shell
* Updated to the latest version of Babel
Expand Down Expand Up @@ -46,7 +49,7 @@ Again, the code is a mess. Please don't look at it and email me that its bad. Fo
* Open a command prompt and navigate to the location where you cloned it.
* Run `npm install`. Electron Shell will be downloaded here, so expect a big download.
* Run `gulp`
* You should now see the inspector. Setup the proxy in your browser of choice (or in your application), and you're good to go!
* You should now see the GUI application. Setup the proxy in your browser of choice (or in your application), and you're good to go!
* From now on you can use `npm start` to run PhoenixMatrix without rebuilding everything.

### Configuring your browser to use the proxy
Expand Down Expand Up @@ -75,6 +78,13 @@ The available options can be found in phoenixmatrix.json, in JSON format (duh!).
if you point your browser to it directly.
* `certificateExpiration`: how many days should the generated certificates be valid for

### Hacking and debugging PhoenixMatrix

PhoenixMatrix is built on top of Electron Shell, which in turn is built on top of Chromium and iojs.
This means you can easily add features or hack it up the same way you would a web page built with React and node/iojs.

Like the Chrome browser, Electron Shell supports the devtools you know: with the window focused, just hit ctrl+shift+i (command+shift+i on mac)!

### Caveats
* On MacOSX, when using the trackpad, scrolling isn't as smooth as it should be.
* On windows, make sure you have OpenSSL installed, and it is in the path. [Git Bash](http://git-scm.com/downloads)
Expand All @@ -87,10 +97,15 @@ even though you imported it, just delete the content of the certificate folder,
upstream for very long.
* Doesn't support old HTTP versions, web sockets, HTTP/2, old servers or uncommon network setups.

### Thanks, inspiration and resources

Inspiration taken from the following projects. Thanks! :
* [node-http-proxy](https://github.com/nodejitsu/node-http-proxy)
* [pem](https://github.com/andris9/pem)

Splitter was ported to React from [bg-splitter](https://github.com/blackgate/bg-splitter). Thanks!
My port isn't polished enough to warrent its own repo, but if anyone is interested, I can do so.

### License

The MIT License (MIT)
Expand Down
4 changes: 2 additions & 2 deletions gulpfile.js
Expand Up @@ -28,7 +28,7 @@ gulp.task('clean', function() {
});

gulp.task('build:styles', function() {
return gulp.src('./stylesheets/style.less')
return gulp.src('./stylesheets/**/*.less')
.pipe(less())
.pipe(gulp.dest('./dist/css'));
});
Expand All @@ -38,7 +38,7 @@ gulp.task('rebuild', function(cb) {
});

gulp.task('build:js', function() {
return gulp.src(['./js/**/*.js'])
return gulp.src(['./js/**/*.js', './js/**/*.jsx'])
.pipe(sourcemaps.init())
.pipe(babel(babelOptions))
.pipe(sourcemaps.write())
Expand Down
93 changes: 3 additions & 90 deletions index.html
Expand Up @@ -4,106 +4,19 @@
<title>PhoenixMatrix Proxy</title>
<link rel="stylesheet" type="text/css" href="./vendor/bootstrap/css/bootstrap.min.css"/>
<link rel="stylesheet" type="text/css" href="./vendor/font-awesome/css/font-awesome.css"/>
<link rel="stylesheet" type="text/css" href="./vendor/splitter/style.css"/>
<link rel="stylesheet" type="text/css" href="./dist/css/splitter.css"/>
<link rel="stylesheet" type="text/css" href="./dist/css/style.css"/>
<script type="text/javascript">
window.$ = window.jQuery = require('./vendor/jquery/jquery.min.js');
</script>
<script type="text/javascript" src='vendor/bootstrap/js/bootstrap.min.js'></script>
<script type="text/javascript" src='vendor/angular/angular.min.js'></script>
<script type="text/javascript" src='vendor/bootstrap-ui/ui-bootstrap-custom-tpls-0.12.0.js'></script>
<script type="text/javascript" src='vendor/splitter/splitter.js'></script>
<script type="text/javascript">
require('./dist/app');
</script>
</head>
<body ng-app="phoenixmatrix">
<body>
<div class="view-wrapper">
<div class="main-section" ng-controller="ProxyCtrl as index">
<div class="header-section navbar navbar-inverse navbar-fixed-top">
<span class="navbar-brand">PhoenixMatrix web debugging proxy v0.1 Preview</span>

<form class="navbar-form navbar-right">
<input type="text" class="form-control" placeholder="Search..." ng-model="requestFilter"
ng-model-options="{ debounce: 500 }">
</form>
</div>
<bg-splitter orientation="horizontal">
<bg-pane min-size="370">
<div class="left-section">
<ul class="button-toolbar">
<li class="fa fa-ban" ng-click="index.clear()" tooltip-placement="right" tooltip="Clear requests"></li>
<li class="fa fa-pause" ng-click="index.togglePause()" ng-class="{ selected: index.paused }" tooltip-placement="right" tooltip="Pause capture"></li>
<li class="fa fa-server" ng-click="index.toggleConnect()" ng-class="{ selected: index.config.includeConnect }" tooltip-placement="right" tooltip="Display CONNECT requests"></li>
</ul>
<div class="requests" request-list requests="proxy.requests" filter="requestFilter"
request-selected="index.selectRequest"></div>
</div>
</bg-pane>
<bg-pane min-size="200">
<div class="content-section">
<div class="request-detail" ng-if="selectedRequest">
<div ng-include="'views/request-header.html'"></div>
<div class="panel panel-default">
<div class="panel-heading">
<h4>Request</h4>
</div>
<div class="panel-body">
<div class="panel panel-primary headers-list" ng-if="selectedRequest.headers">
<div class="panel-heading" ng-click="collapseRequestHeaders = !collapseRequestHeaders">
Headers
</div>
<table class="table table-hover" ng-if="!collapseRequestHeaders">
<tbody>
<tr ng-repeat="(key, value) in selectedRequest.headers">
<td ng-bind="key"></td>
<td ng-bind="value"></td>
</tr>
</tbody>
</table>
</div>
<div class="panel panel-primary request-body" ng-if="selectedRequest.body.length">
<div class="panel-heading" ng-click="showRequestBody = !showRequestBody">Body</div>
<div class="panel-body" ng-if="showRequestBody" ng-bind="selectedRequest.body"></div>
</div>
</div>
</div>
</div>
<div class="response-detail" ng-if="selectedRequest && selectedRequest.responseHeaders">
<div class="panel panel-default">
<div class="panel-heading">
<h4>Response</h4>
</div>
<div class="panel-body">
<div class="panel panel-primary headers-list">
<div class="panel-heading"
ng-click="collapseResponseHeaders = !collapseResponseHeaders">Headers
</div>
<table class="table table-hover" ng-if="!collapseResponseHeaders">
<tbody>
<tr ng-repeat="(key, value) in selectedRequest.responseHeaders">
<td ng-bind="key"></td>
<td ng-bind="value"></td>
</tr>
</tbody>
</table>
</div>
<div class="panel panel-primary response-body" ng-if="selectedRequest.responseBody">
<div class="panel-heading" ng-click="collapseResponseBody = !collapseResponseBody">
Body
</div>
<div class="panel-body" ng-if="collapseResponseBody">
<textarea ng-bind="selectedRequest.responseBody"></textarea>
</div>
</div>
</div>
</div>
</div>
</div>
</bg-pane>
</div>
<div class="footer-section"></div>
</div>
<div class="main-section"></div>
</div>
</body>
</html>
10 changes: 6 additions & 4 deletions index.js
Expand Up @@ -18,9 +18,11 @@ app.on('window-all-closed', function() {
});

app.on('ready', function() {
globalShortcut.register('ctrl+shift+i', function() {
console.log('opening debugger')
BrowserWindow.getFocusedWindow().toggleDevTools();
globalShortcut.register('CommandOrControl+Shift+i', function() {
var w = BrowserWindow.getFocusedWindow();
if(w) {
w.toggleDevTools();
}
});

mainWindow = new BrowserWindow({width: 1000, height: 800, icon: "./icon.png"});
Expand All @@ -29,4 +31,4 @@ app.on('ready', function() {
mainWindow.on('closed', function() {
mainWindow = null;
});
});
});
10 changes: 10 additions & 0 deletions js/actions/config-actions.js
@@ -0,0 +1,10 @@
import AppDispatcher from '../dispatchers/AppDispatcher';
import configConstants from '../constants/config-constants';

export default {
toggleConnect: function() {
AppDispatcher.dispatch({
action: configConstants.TOGGLE_CONNECT
});
}
};
30 changes: 30 additions & 0 deletions js/actions/request-actions.js
@@ -0,0 +1,30 @@
import AppDispatcher from '../dispatchers/AppDispatcher';
import requestConstants from '../constants/request-constants';

export default {
selectRequest: function(request) {
AppDispatcher.dispatch({
action: requestConstants.SELECT_REQUEST,
request: request
});
},

setFilter: function(expression) {
AppDispatcher.dispatch({
action: requestConstants.SET_FILTER,
expression
});
},

clear: function() {
AppDispatcher.dispatch({
action: requestConstants.CLEAR
});
},

togglePause: function() {
AppDispatcher.dispatch({
action: requestConstants.TOGGLE_PAUSE
});
}
};
16 changes: 11 additions & 5 deletions js/app.js
@@ -1,8 +1,14 @@
import 'babel/polyfill';
import './lib/angular';
import './services';
import './filters';
import './directives';
import './controllers';
import React from 'react';
import domready from "domready";
import PhoenixMatrixApp from './components/PhoenixMatrixApp';
import requestStore from './stores/request-store';

requestStore.init();

domready(function () {
React.render(
<PhoenixMatrixApp />,
document.body
);
});
9 changes: 9 additions & 0 deletions js/components/Footer.jsx
@@ -0,0 +1,9 @@
import React from 'react';

export default React.createClass({
render: function() {
return (
<div className="footer-section"></div>
);
}
});
29 changes: 29 additions & 0 deletions js/components/Header.jsx
@@ -0,0 +1,29 @@
import _ from 'lodash';

import React from 'react';
import requestActions from '../actions/request-actions';

export default React.createClass({
setFilter: function(expression) {
requestActions.setFilter(expression.trim());
},

onChange: function(e) {
this.setFilter(e.target.value);
},

componentWillMount: function() {
this.setFilter = _.debounce(this.setFilter, 500);
},

render: function() {
return (
<div className="header-section navbar navbar-inverse navbar-fixed-top">
<span className="navbar-brand">PhoenixMatrix web debugging proxy v0.1 Preview</span>
<form className="navbar-form navbar-right">
<input ref="searchInput" type="text" className="form-control" placeholder="Search..." onChange={this.onChange} />
</form>
</div>
);
}
});
49 changes: 49 additions & 0 deletions js/components/HttpHeaders.jsx
@@ -0,0 +1,49 @@
import React from 'react';
import _ from 'lodash';

const PureRenderMixin = React.addons.PureRenderMixin;

export default React.createClass({
mixins: [PureRenderMixin],
getInitialState: function() {
return { showTable: true };
},

toggle: function() {
this.setState({showTable: !this.state.showTable});
},

render: function() {
const headers = this.props.headers;

let content = null;
let headerTable;

if(headers && Object.keys(headers).length) {
if(this.state.showTable) {
headerTable =
<table className="table table-hover" ng-if="!collapseRequestHeaders">
<tbody>
{
_.map(headers, function(value, key) {
return <tr key={key}>
<td>{key}</td>
<td>{value}</td>
</tr>;
})
}
</tbody>
</table>
}

content = <div className="panel panel-primary headers-list">
<div className="panel-heading" onClick={this.toggle}>
Headers
</div>
{headerTable}
</div>;
}

return content;
}
});
21 changes: 21 additions & 0 deletions js/components/LeftSection.jsx
@@ -0,0 +1,21 @@
import React from 'react';

import VerticalButtonBar from './VerticalButtonBar';
import RequestList from './RequestList';

const PureRenderMixin = React.addons.PureRenderMixin;

export default React.createClass({
mixins: [PureRenderMixin],
render: function() {
return (
<div className="left-section">
<VerticalButtonBar paused={this.props.paused} config={this.props.config} />
<RequestList
requests={this.props.requests}
selectedRequest={this.props.selectedRequest}
/>
</div>
);
}
});

0 comments on commit 2d649e1

Please sign in to comment.