mirror of
https://github.com/gravitational/teleport
synced 2024-10-22 02:03:24 +00:00
(web) Adding a terminal host
This commit is contained in:
parent
57f8887ba2
commit
0b4565b018
813
web/dist/app/app.js
vendored
813
web/dist/app/app.js
vendored
File diff suppressed because one or more lines are too long
26
web/dist/app/styles.js
vendored
26
web/dist/app/styles.js
vendored
File diff suppressed because one or more lines are too long
2602
web/dist/app/vendor.js
vendored
2602
web/dist/app/vendor.js
vendored
File diff suppressed because one or more lines are too long
6
web/dist/index.html
vendored
6
web/dist/index.html
vendored
|
@ -10,11 +10,11 @@
|
|||
<script src="/web/app/assets/js/jquery-validate-1.14.0.js"></script>
|
||||
<script src="/web/app/assets/js/bootstrap.js"></script>
|
||||
<script src="/web/app/assets/js/term.js"></script>
|
||||
<script src="/web/app/vendor.js?ver=0.11456438265918"></script>
|
||||
<script src="/web/app/styles.js?ver=0.11456438265918"></script>
|
||||
<script src="/web/app/vendor.js?ver=0.11456463877805"></script>
|
||||
<script src="/web/app/styles.js?ver=0.11456463877805"></script>
|
||||
</head>
|
||||
<body class="grv">
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
<script src="/web/app/app.js?ver=0.11456438265918"></script>
|
||||
<script src="/web/app/app.js?ver=0.11456463877805"></script>
|
||||
</html>
|
||||
|
|
|
@ -3,7 +3,7 @@ var session = require('./session');
|
|||
var cfg = require('app/config');
|
||||
var $ = require('jQuery');
|
||||
|
||||
const refreshRate = 60000 * 1; // 1 min
|
||||
const refreshRate = 60000 * 100; // 1 min
|
||||
|
||||
var refreshTokenTimerId = null;
|
||||
|
||||
|
|
|
@ -1,20 +1,17 @@
|
|||
var React = require('react');
|
||||
var NavLeftBar = require('./navLeftBar');
|
||||
var cfg = require('app/config');
|
||||
var {TerminalHost} = require('./terminalHost.jsx');
|
||||
|
||||
var App = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<div className="grv-tlpt">
|
||||
<TerminalHost/>
|
||||
<NavLeftBar/>
|
||||
<div className="row">
|
||||
<nav className="" role="navigation" style={{ marginBottom: 0 }}>
|
||||
<ul className="nav navbar-top-links navbar-right">
|
||||
<li>
|
||||
<span className="m-r-sm text-muted welcome-message">
|
||||
Welcome to Gravitational Portal
|
||||
</span>
|
||||
</li>
|
||||
<ul className="nav navbar-top-links navbar-right">
|
||||
<li>
|
||||
<a href={cfg.routes.logout}>
|
||||
<i className="fa fa-sign-out"></i>
|
||||
|
|
|
@ -3,8 +3,9 @@ var { Router, IndexLink, History } = require('react-router');
|
|||
var cfg = require('app/config');
|
||||
|
||||
var menuItems = [
|
||||
{icon: 'fa fa fa-sitemap', to: cfg.routes.nodes, title: 'Nodes'},
|
||||
{icon: 'fa fa-hdd-o', to: cfg.routes.sessions, title: 'Sessions'}
|
||||
{icon: 'fa fa-cogs', to: cfg.routes.nodes, title: 'Nodes'},
|
||||
{icon: 'fa fa-sitemap', to: cfg.routes.sessions, title: 'Sessions'},
|
||||
{icon: 'fa fa-question', to: cfg.routes.sessions, title: 'Sessions'},
|
||||
];
|
||||
|
||||
var NavLeftBar = React.createClass({
|
||||
|
|
|
@ -3,6 +3,7 @@ var reactor = require('app/reactor');
|
|||
var {getters, actions} = require('app/modules/nodes');
|
||||
var userGetters = require('app/modules/user/getters');
|
||||
var {Table, Column, Cell} = require('app/components/table.jsx');
|
||||
var {connect} = require('app/modules/activeTerminal/actions');
|
||||
|
||||
const TextCell = ({rowIndex, data, columnKey, ...props}) => (
|
||||
<Cell {...props}>
|
||||
|
@ -21,7 +22,7 @@ const TagCell = ({rowIndex, data, columnKey, ...props}) => (
|
|||
</Cell>
|
||||
);
|
||||
|
||||
const LoginCell = ({user, ...props}) => {
|
||||
const LoginCell = ({user, rowIndex, data, ...props}) => {
|
||||
if(!user || user.logins.length === 0){
|
||||
return <Cell {...props} />;
|
||||
}
|
||||
|
@ -29,13 +30,13 @@ const LoginCell = ({user, ...props}) => {
|
|||
var $lis = [];
|
||||
|
||||
for(var i = 0; i < user.logins.length; i++){
|
||||
$lis.push(<li key={i}><a href="#" target="_blank">{user.logins[i]}</a></li>);
|
||||
$lis.push(<li key={i}><a href="#" target="_blank" onClick={connect.bind(null, data[rowIndex].addr, user.logins[i])}>{user.logins[i]}</a></li>);
|
||||
}
|
||||
|
||||
return (
|
||||
<Cell {...props}>
|
||||
<div className="btn-group">
|
||||
<button type="button" className="btn btn-sm btn-primary">{user.logins[0]}</button>
|
||||
<button type="button" onClick={connect.bind(null, data[rowIndex].addr, user.logins[0])} className="btn btn-sm btn-primary">{user.logins[0]}</button>
|
||||
{
|
||||
$lis.length > 1 ? (
|
||||
<div className="btn-group">
|
||||
|
@ -80,14 +81,14 @@ var Nodes = React.createClass({
|
|||
<div className="">
|
||||
<div className="">
|
||||
<div className="">
|
||||
<Table rowCount={data.length} className="grv-nodes-table">
|
||||
<Table rowCount={data.length} className="table-stripped grv-nodes-table">
|
||||
<Column
|
||||
columnKey="count"
|
||||
columnKey="sessionCount"
|
||||
header={<Cell> Sessions </Cell> }
|
||||
cell={<TextCell data={data}/> }
|
||||
/>
|
||||
<Column
|
||||
columnKey="ip"
|
||||
columnKey="addr"
|
||||
header={<Cell> Node </Cell> }
|
||||
cell={<TextCell data={data}/> }
|
||||
/>
|
||||
|
@ -99,7 +100,7 @@ var Nodes = React.createClass({
|
|||
<Column
|
||||
columnKey="roles"
|
||||
header={<Cell>Login as</Cell> }
|
||||
cell={<LoginCell user={this.state.user}/> }
|
||||
cell={<LoginCell data={data} user={this.state.user}/> }
|
||||
/>
|
||||
</Table>
|
||||
</div>
|
||||
|
|
|
@ -56,7 +56,7 @@ var GrvTable = React.createClass({
|
|||
children.push(child);
|
||||
});
|
||||
|
||||
var tableClass = 'table table-bordered ' + this.props.className;
|
||||
var tableClass = 'table ' + this.props.className;
|
||||
|
||||
return (
|
||||
<table className={tableClass}>
|
||||
|
|
107
web/src/app/components/terminalHost.jsx
Normal file
107
web/src/app/components/terminalHost.jsx
Normal file
|
@ -0,0 +1,107 @@
|
|||
var session = require('app/session');
|
||||
var cfg = require('app/config');
|
||||
var React = require('react');
|
||||
var {getters, actions} = require('app/modules/activeTerminal/');
|
||||
|
||||
var TerminalHost = React.createClass({
|
||||
|
||||
mixins: [reactor.ReactMixin],
|
||||
|
||||
getDataBindings() {
|
||||
return {
|
||||
terminal: getters.terminal
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
if(!this.state.terminal){
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="grv-terminal-host">
|
||||
<div className="grv-terminal-participans">
|
||||
<ul className="nav">
|
||||
<li><button className="btn btn-primary btn-circle" type="button"> <strong>A</strong></button></li>
|
||||
<li><button className="btn btn-primary btn-circle" type="button"> B </button></li>
|
||||
<li><button className="btn btn-primary btn-circle" type="button"> C </button></li>
|
||||
<li>
|
||||
<button onClick={actions.close} className="btn btn-danger btn-circle" type="button">
|
||||
<i className="fa fa-times"></i>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<TerminalBox settings={this.state.terminal} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var TerminalBox = React.createClass({
|
||||
renderTerminal: function() {
|
||||
var {token} = session.getUserData();
|
||||
var parent = document.getElementById("terminal-box");
|
||||
|
||||
var settings = this.props.settings;
|
||||
//settings.sid = 5555;
|
||||
settings.term = {
|
||||
h: 120,
|
||||
w: 100
|
||||
};
|
||||
|
||||
var connectionStr = cfg.api.getTermConnString(token, settings);
|
||||
|
||||
this.term = new Terminal({
|
||||
cols: 180,
|
||||
rows: 50,
|
||||
useStyle: true,
|
||||
screenKeys: true,
|
||||
cursorBlink: false
|
||||
});
|
||||
|
||||
this.term.open(parent);
|
||||
this.socket = new WebSocket(connectionStr, "proto");
|
||||
this.term.write('\x1b[94mconnecting to "pod"\x1b[m\r\n');
|
||||
|
||||
this.socket.onopen = () => {
|
||||
this.term.on('data', (data) => {
|
||||
this.socket.send(data);
|
||||
});
|
||||
|
||||
this.socket.onmessage = (e) => {
|
||||
this.term.write(e.data);
|
||||
}
|
||||
|
||||
this.socket.onclose = () => {
|
||||
this.term.write('\x1b[31mdisconnected\x1b[m\r\n');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.renderTerminal();
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
this.socket.close();
|
||||
this.term.destroy();
|
||||
},
|
||||
|
||||
shouldComponentUpdate: function() {
|
||||
return false;
|
||||
},
|
||||
|
||||
componentWillReceiveProps: function(props) {
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div className="grv-wiz-terminal" id="terminal-box">
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default TerminalHost;
|
||||
export {TerminalBox, TerminalHost};
|
|
@ -11,6 +11,13 @@ let cfg = {
|
|||
createUserPath: '/v1/webapi/users',
|
||||
getInviteUrl: (inviteToken) => {
|
||||
return formatPattern(cfg.api.invitePath, {inviteToken});
|
||||
},
|
||||
|
||||
getTermConnString: (token, params) => {
|
||||
var json = JSON.stringify(params);
|
||||
var jsonEncoded = window.encodeURI(json);
|
||||
var prefix = location.protocol == "https:"?"wss://":"ws://";
|
||||
return `${prefix}${params.addr}/v1/webapi/sites/-current-/connect?access_token=${token}¶ms=${jsonEncoded}`;
|
||||
}
|
||||
},
|
||||
|
||||
|
|
6
web/src/app/modules/activeTerminal/actionTypes.js
Normal file
6
web/src/app/modules/activeTerminal/actionTypes.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
import keyMirror from 'keymirror'
|
||||
|
||||
export default keyMirror({
|
||||
TLPT_TERM_CONNECT: null,
|
||||
TLPT_TERM_CLOSE: null
|
||||
})
|
21
web/src/app/modules/activeTerminal/actions.js
Normal file
21
web/src/app/modules/activeTerminal/actions.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
var reactor = require('app/reactor');
|
||||
var { TLPT_TERM_CONNECT, TLPT_TERM_CLOSE } = require('./actionTypes');
|
||||
|
||||
export default {
|
||||
|
||||
close(){
|
||||
reactor.dispatch(TLPT_TERM_CLOSE);
|
||||
},
|
||||
|
||||
connect(addr, login){
|
||||
/*
|
||||
* {
|
||||
* "addr": "127.0.0.1:5000",
|
||||
* "login": "admin",
|
||||
* "term": {"h": 120, "w": 100},
|
||||
* "sid": "123"
|
||||
* }
|
||||
*/
|
||||
reactor.dispatch(TLPT_TERM_CONNECT, {addr, login});
|
||||
}
|
||||
}
|
22
web/src/app/modules/activeTerminal/activeTermStore.js
Normal file
22
web/src/app/modules/activeTerminal/activeTermStore.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
var { Store, toImmutable } = require('nuclear-js');
|
||||
var { TLPT_TERM_CONNECT, TLPT_TERM_CLOSE } = require('./actionTypes');
|
||||
|
||||
export default Store({
|
||||
getInitialState() {
|
||||
return toImmutable(null);
|
||||
},
|
||||
|
||||
initialize() {
|
||||
this.on(TLPT_TERM_CONNECT, connect);
|
||||
this.on(TLPT_TERM_CLOSE, close);
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
function close(){
|
||||
return toImmutable(null);
|
||||
}
|
||||
|
||||
function connect(state, term){
|
||||
return toImmutable(term);
|
||||
}
|
13
web/src/app/modules/activeTerminal/getters.js
Normal file
13
web/src/app/modules/activeTerminal/getters.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
const terminal = [ ['tlpt_active_terminal'], (settings) => {
|
||||
if(!settings){
|
||||
return null;
|
||||
}
|
||||
|
||||
var {addr, login } = settings.toJS();
|
||||
return {addr, login }
|
||||
}
|
||||
];
|
||||
|
||||
export default {
|
||||
terminal
|
||||
}
|
3
web/src/app/modules/activeTerminal/index.js
Normal file
3
web/src/app/modules/activeTerminal/index.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
module.exports.getters = require('./getters');
|
||||
module.exports.actions = require('./actions');
|
||||
module.exports.activeTermStore = require('./activeTermStore');
|
|
@ -1,5 +1,6 @@
|
|||
var reactor = require('app/reactor');
|
||||
reactor.registerStores({
|
||||
'tlpt_active_terminal': require('./activeTerminal/activeTermStore'),
|
||||
'tlpt_user': require('./user/userStore'),
|
||||
'tlpt_nodes': require('./nodes/nodeStore'),
|
||||
'tlpt_invite': require('./invite/inviteStore'),
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
//var sort = require('app/common/sort');
|
||||
var { toImmutable } = require('nuclear-js');
|
||||
|
||||
const nodeListView = [ ['tlpt_nodes'], (nodes) =>{
|
||||
return nodes.map((item)=>{
|
||||
var sessions = item.get('sessions') || toImmutable([]);
|
||||
return {
|
||||
tags: getTags(item),
|
||||
ip: item.get('addr')
|
||||
tags: getTags(item.get('node')),
|
||||
addr: item.getIn(['node', 'addr']),
|
||||
sessionCount: sessions.size
|
||||
}
|
||||
}).toJS();
|
||||
}
|
||||
|
|
|
@ -9,11 +9,11 @@ var cfg = require('app/config');
|
|||
export default {
|
||||
|
||||
ensureUser(nextState, replace, cb){
|
||||
/*var userData = session.getUserData();
|
||||
var userData = session.getUserData();
|
||||
reactor.dispatch(TLPT_RECEIVE_USER, userData.user);
|
||||
cb();*/
|
||||
cb();
|
||||
|
||||
auth.ensureUser()
|
||||
/*auth.ensureUser()
|
||||
.done((userData)=> {
|
||||
reactor.dispatch(TLPT_RECEIVE_USER, userData.user);
|
||||
cb();
|
||||
|
@ -21,7 +21,7 @@ export default {
|
|||
.fail(()=>{
|
||||
replace({redirectTo: nextState.location.pathname }, cfg.routes.login);
|
||||
cb();
|
||||
});
|
||||
});*/
|
||||
},
|
||||
|
||||
signUp({name, psw, token, inviteToken}){
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
@import "grv-login";
|
||||
@import "grv-logo";
|
||||
@import "grv-nodes";
|
||||
|
||||
@import "grv-terminal-host";
|
||||
|
||||
.grv {
|
||||
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
.grv-nodes{
|
||||
|
||||
|
||||
h1{
|
||||
margin-top: -30px;
|
||||
padding-bottom: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.grv-nodes-table{
|
||||
.label {
|
||||
margin-right: 5px;
|
||||
|
|
25
web/src/styles/grv-terminal-host.scss
Normal file
25
web/src/styles/grv-terminal-host.scss
Normal file
|
@ -0,0 +1,25 @@
|
|||
.grv-terminal-host{
|
||||
padding: 50px 60px;
|
||||
padding-left: 130px;
|
||||
position: fixed;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: block;
|
||||
background-color: black;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
|
||||
.grv-terminal-participans{
|
||||
margin-left: -100px;
|
||||
width: 50px;
|
||||
position: absolute;
|
||||
|
||||
.nav {
|
||||
text-align: center;
|
||||
|
||||
> li{
|
||||
margin: 15px 0;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue