diff options
Diffstat (limited to 'web')
-rw-r--r-- | web/src/css/flowtable.less | 153 | ||||
-rw-r--r-- | web/src/css/tabs.less | 6 | ||||
-rw-r--r-- | web/src/js/components/flowdetail.jsx.js | 79 | ||||
-rw-r--r-- | web/src/js/components/flowtable-columns.jsx.js | 6 | ||||
-rw-r--r-- | web/src/js/components/header.jsx.js | 103 | ||||
-rw-r--r-- | web/src/js/components/mainview.jsx.js | 13 |
6 files changed, 235 insertions, 125 deletions
diff --git a/web/src/css/flowtable.less b/web/src/css/flowtable.less index 38f5e0e8..9988f1a8 100644 --- a/web/src/css/flowtable.less +++ b/web/src/css/flowtable.less @@ -1,78 +1,95 @@ .flow-table { - width: 100%; - overflow: auto; + width: 100%; + overflow: auto; - table { - width: 100%; - table-layout: fixed; - } + table { + width: 100%; + table-layout: fixed; + } - thead { - background-color: #F2F2F2; - line-height: 23px; - } + thead { + background-color: #F2F2F2; + line-height: 23px; + } - th { - font-weight: normal; - box-shadow: 0 1px 0 #a6a6a6; - } + th { + font-weight: normal; + box-shadow: 0 1px 0 #a6a6a6; + } - tr { - cursor: pointer; + tr { + cursor: pointer; - &:nth-child(even) { - background-color : rgba(0,0,0,0.05); - } - &.selected { - background-color: hsla(209, 52%, 84%, 0.5) !important; - } - &.highlighted { - background-color: hsla(48, 100%, 50%, 0.4); - } - &.highlighted:nth-child(even) { - background-color: hsla(48, 100%, 50%, 0.5); - } - } + &:nth-child(even) { + background-color: rgba(0, 0, 0, 0.05); + } + &.selected { + background-color: hsla(209, 52%, 84%, 0.5) !important; + } + &.highlighted { + background-color: hsla(48, 100%, 50%, 0.4); + } + &.highlighted:nth-child(even) { + background-color: hsla(48, 100%, 50%, 0.5); + } + } - td { - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - } + td { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } - tr.intercepted:not(.has-response) { - .col-path, .col-method { - color: hsl(30, 100%, 50%); - } - } - tr.intercepted.has-response { - .col-status, .col-size, .col-time { - color: hsl(30, 100%, 50%); - } - } + @interceptorange: hsl(30, 100%, 50%); - .col-tls { - width: 10px; - } - .col-tls-https { - background-color: rgba(0, 185, 0, 0.5); - } - .col-icon { - width: 32px; - } - .col-method { - width: 60px; - } - .col-status { - width: 50px; - } - .col-size { - width: 70px; - } - .col-time { - width: 50px; - } - td.col-time, td.col-size { - text-align: right; - } + tr.intercepted:not(.has-response) { + .col-path, .col-method { + color: @interceptorange; + } + } + tr.intercepted.has-response { + .col-status, .col-size, .col-time { + color: @interceptorange; + } + } + + .fa { + line-height: inherit; + &.pull-right { + margin-left: 0; + } + } + + .col-tls { + width: 10px; + } + .col-tls-https { + background-color: rgba(0, 185, 0, 0.5); + } + .col-icon { + width: 32px; + } + .col-path { + .fa-repeat { + color: green; + } + .fa-pause { + color: @interceptorange; + } + } + .col-method { + width: 60px; + } + .col-status { + width: 50px; + } + .col-size { + width: 70px; + } + .col-time { + width: 50px; + } + td.col-time, td.col-size { + text-align: right; + } }
\ No newline at end of file diff --git a/web/src/css/tabs.less b/web/src/css/tabs.less index 36bc5b68..43f7264e 100644 --- a/web/src/css/tabs.less +++ b/web/src/css/tabs.less @@ -40,6 +40,10 @@ a { padding: 0px 7px; margin: 2px 2px -1px; - + } + a.nav-action { + float: right; + padding: 0; + margin: 1px 0 0px; } }
\ No newline at end of file diff --git a/web/src/js/components/flowdetail.jsx.js b/web/src/js/components/flowdetail.jsx.js index 5727ec75..dfc0099e 100644 --- a/web/src/js/components/flowdetail.jsx.js +++ b/web/src/js/components/flowdetail.jsx.js @@ -1,7 +1,72 @@ +var DeleteButton = React.createClass({ + onClick: function (e) { + e.preventDefault(); + FlowActions.delete(this.props.flow); + }, + render: function () { + return ( + <a title="[d]elete Flow" + href="#" + className="nav-action" + onClick={this.onClick}> + <i className="fa fa-fw fa-trash"></i> + </a> + ); + } +}); +var DuplicateButton = React.createClass({ + onClick: function (e) { + e.preventDefault(); + FlowActions.duplicate(this.props.flow); + }, + render: function () { + return ( + <a title="[D]uplicate Flow" + href="#" + className="nav-action" + onClick={this.onClick}> + <i className="fa fa-fw fa-edit"></i> + </a> + ); + } +}); +var ReplayButton = React.createClass({ + onClick: function (e) { + e.preventDefault(); + FlowActions.replay(this.props.flow); + }, + render: function () { + return ( + <a title="[r]eplay Flow" + href="#" + className="nav-action" + onClick={this.onClick}> + <i className="fa fa-fw fa-close"></i> + </a> + ); + } +}); +var AcceptButton = React.createClass({ + onClick: function (e) { + e.preventDefault(); + FlowActions.accept(this.props.flow); + }, + render: function () { + return ( + <a title="[a]ccept (resume) Flow" + href="#" + className="nav-action" + onClick={this.onClick}> + <i className="fa fa-fw fa-play"></i> + </a> + ); + } +}); var FlowDetailNav = React.createClass({ render: function () { + var flow = this.props.flow; - var items = this.props.tabs.map(function (e) { + var tabs = this.props.tabs.map(function (e) { var str = e.charAt(0).toUpperCase() + e.slice(1); var className = this.props.active === e ? "active" : ""; var onClick = function (event) { @@ -13,9 +78,14 @@ var FlowDetailNav = React.createClass({ className={className} onClick={onClick}>{str}</a>; }.bind(this)); + return ( <nav ref="head" className="nav-tabs nav-tabs-sm"> - {items} + {tabs} + <DeleteButton flow={flow}/> + <DuplicateButton flow={flow}/> + <ReplayButton flow={flow}/> + { flow.intercepted ? <AcceptButton flow={this.props.flow}/> : null } </nav> ); } @@ -104,7 +174,9 @@ var FlowDetailError = React.createClass({ <section> <div className="alert alert-warning"> {flow.error.msg} - <div><small>{ formatTimeStamp(flow.error.timestamp) }</small></div> + <div> + <small>{ formatTimeStamp(flow.error.timestamp) }</small> + </div> </div> </section> ); @@ -341,6 +413,7 @@ var FlowDetail = React.createClass({ return ( <div className="flow-detail" onScroll={this.adjustHead}> <FlowDetailNav ref="head" + flow={flow} tabs={tabs} active={active} selectTab={this.selectTab}/> diff --git a/web/src/js/components/flowtable-columns.jsx.js b/web/src/js/components/flowtable-columns.jsx.js index 1aa256c4..9162e077 100644 --- a/web/src/js/components/flowtable-columns.jsx.js +++ b/web/src/js/components/flowtable-columns.jsx.js @@ -66,7 +66,11 @@ var PathColumn = React.createClass({ }, render: function () { var flow = this.props.flow; - return <td className="col-path">{flow.request.scheme + "://" + flow.request.host + flow.request.path}</td>; + return <td className="col-path"> + {flow.request.is_replay ? <i className="fa fa-fw fa-repeat pull-right"></i> : null} + {flow.intercepted ? <i className="fa fa-fw fa-pause pull-right"></i> : null} + {flow.request.scheme + "://" + flow.request.host + flow.request.path} + </td>; } }); diff --git a/web/src/js/components/header.jsx.js b/web/src/js/components/header.jsx.js index 9e6b8f2f..e1016950 100644 --- a/web/src/js/components/header.jsx.js +++ b/web/src/js/components/header.jsx.js @@ -111,23 +111,9 @@ var FilterInput = React.createClass({ var MainMenu = React.createClass({ mixins: [Navigation, State], statics: { - title: "Traffic", + title: "Start", route: "flows" }, - toggleEventLog: function () { - var d = {}; - - if(this.getQuery()[Query.SHOW_EVENTLOG]){ - d[Query.SHOW_EVENTLOG] = undefined; - } else { - d[Query.SHOW_EVENTLOG] = "t"; // any non-false value will do it, keep it short - } - - this.setQuery(d); - }, - clearFlows: function () { - FlowActions.clear(); - }, onFilterChange: function (val) { var d = {}; d[Query.FILTER] = val; @@ -145,22 +131,9 @@ var MainMenu = React.createClass({ var filter = this.getQuery()[Query.FILTER] || ""; var highlight = this.getQuery()[Query.HIGHLIGHT] || ""; var intercept = this.props.settings.intercept || ""; - var showEventLog = this.getQuery()[Query.SHOW_EVENTLOG]; return ( <div> - <button - className={"btn " + (showEventLog ? "btn-primary" : "btn-default")} - onClick={this.toggleEventLog}> - <i className="fa fa-database"></i> - Display Event Log - </button> - <span> </span> - <button className="btn btn-default" onClick={this.clearFlows}> - <i className="fa fa-eraser"></i> - Clear Flows - </button> - <span> </span> <form className="form-inline" style={{display: "inline"}}> <FilterInput placeholder="Filter" @@ -189,13 +162,36 @@ var MainMenu = React.createClass({ }); -var ToolsMenu = React.createClass({ +var ViewMenu = React.createClass({ statics: { - title: "Tools", + title: "View", route: "flows" }, + mixins: [Navigation, State], + toggleEventLog: function () { + var d = {}; + + if (this.getQuery()[Query.SHOW_EVENTLOG]) { + d[Query.SHOW_EVENTLOG] = undefined; + } else { + d[Query.SHOW_EVENTLOG] = "t"; // any non-false value will do it, keep it short + } + + this.setQuery(d); + }, render: function () { - return <div>Tools Menu</div>; + var showEventLog = this.getQuery()[Query.SHOW_EVENTLOG]; + return ( + <div> + <button + className={"btn " + (showEventLog ? "btn-primary" : "btn-default")} + onClick={this.toggleEventLog}> + <i className="fa fa-database"></i> + Show Eventlog + </button> + <span> </span> + </div> + ); } }); @@ -232,7 +228,9 @@ var FileMenu = React.createClass({ }, handleNewClick: function (e) { e.preventDefault(); - console.error("unimplemented: handleNewClick"); + if (confirm("Delete all flows?")) { + FlowActions.clear(); + } }, handleOpenClick: function (e) { e.preventDefault(); @@ -259,25 +257,34 @@ var FileMenu = React.createClass({ New </a> </li> - <li> - <a href="#" onClick={this.handleOpenClick}> - <i className="fa fa-fw fa-folder-open"></i> - Open - </a> - </li> - <li> - <a href="#" onClick={this.handleSaveClick}> - <i className="fa fa-fw fa-save"></i> - Save - </a> - </li> <li role="presentation" className="divider"></li> <li> - <a href="#" onClick={this.handleShutdownClick}> - <i className="fa fa-fw fa-plug"></i> - Shutdown + <a href="http://mitm.it/" target="_blank"> + <i className="fa fa-fw fa-lock"></i> + Install Certificates... </a> </li> + {/* + <li> + <a href="#" onClick={this.handleOpenClick}> + <i className="fa fa-fw fa-folder-open"></i> + Open + </a> + </li> + <li> + <a href="#" onClick={this.handleSaveClick}> + <i className="fa fa-fw fa-save"></i> + Save + </a> + </li> + <li role="presentation" className="divider"></li> + <li> + <a href="#" onClick={this.handleShutdownClick}> + <i className="fa fa-fw fa-plug"></i> + Shutdown + </a> + </li> + */} </ul> </div> ); @@ -285,7 +292,7 @@ var FileMenu = React.createClass({ }); -var header_entries = [MainMenu, ToolsMenu, ReportsMenu]; +var header_entries = [MainMenu, ViewMenu /*, ReportsMenu */]; var Header = React.createClass({ diff --git a/web/src/js/components/mainview.jsx.js b/web/src/js/components/mainview.jsx.js index 78415ad0..41f22a95 100644 --- a/web/src/js/components/mainview.jsx.js +++ b/web/src/js/components/mainview.jsx.js @@ -44,6 +44,7 @@ var MainView = React.createClass({ view.addListener("recalculate", this.onRecalculate); view.addListener("add update remove", this.onUpdate); + view.addListener("remove", this.onRemove); }, onRecalculate: function () { this.forceUpdate(); @@ -57,6 +58,12 @@ var MainView = React.createClass({ this.forceUpdate(); } }, + onRemove: function (flow_id, index) { + if (flow_id === this.getParams().flowId) { + var flow_to_select = this.state.view.list[Math.min(index, this.state.view.list.length -1)]; + this.selectFlow(flow_to_select); + } + }, closeView: function () { this.state.view.close(); }, @@ -106,7 +113,7 @@ var MainView = React.createClass({ }, onKeyDown: function (e) { var flow = this.getSelected(); - if(e.ctrlKey){ + if (e.ctrlKey) { return; } switch (e.keyCode) { @@ -157,8 +164,6 @@ var MainView = React.createClass({ if (e.shiftKey) { FlowActions.duplicate(flow); } else { - var last_flow = this.state.view.index(flow) === this.state.view.list.length - 1; - this.selectFlowRelative(last_flow ? -1 : +1); FlowActions.delete(flow); } } @@ -171,7 +176,7 @@ var MainView = React.createClass({ } break; case Key.R: - if(!e.shiftKey && flow){ + if (!e.shiftKey && flow) { FlowActions.replay(flow); } break; |