Merge branch 'alexander/ids' of github.com:gravitational/teleport into alexander/ids

This commit is contained in:
klizhentas 2016-03-04 10:37:21 -08:00
commit 7e70589335
37 changed files with 1989 additions and 1588 deletions

View file

@ -244,7 +244,6 @@ func setCmdUser(cmd *exec.Cmd, username string) error {
if err != nil {
return trace.Wrap(err)
}
cmd.Env = []string{"HOME=" + osUser.HomeDir}
// are we already username?
curUser, err := user.Current()

1172
web/dist/app/app.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1855
web/dist/app/vendor.js vendored

File diff suppressed because one or more lines are too long

6
web/dist/index.html vendored
View file

@ -11,11 +11,11 @@
<script src="/web/app/assets/js/underscore-1.8.3.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.11456968818190"></script>
<script src="/web/app/styles.js?ver=0.11456968818190"></script>
<script src="/web/app/vendor.js?ver=0.11457083126376"></script>
<script src="/web/app/styles.js?ver=0.11457083126376"></script>
</head>
<body class="grv">
<div id="app"></div>
</body>
<script src="/web/app/app.js?ver=0.11456968818190"></script>
<script src="/web/app/app.js?ver=0.11457083126376"></script>
</html>

View file

@ -5,9 +5,9 @@ var {actions} = require('app/modules/activeTerminal/');
class Tty extends EventEmitter {
constructor({addr, login, sid, rows, cols }){
constructor({serverId, login, sid, rows, cols }){
super();
this.options = { addr, login, sid, rows, cols };
this.options = { serverId, login, sid, rows, cols };
this.socket = null;
}

View file

@ -3,8 +3,10 @@ var {getters, actions} = require('app/modules/activeTerminal/');
var EventStreamer = require('./eventStreamer.jsx');
var Tty = require('app/common/tty');
var TtyTerminal = require('./../terminal.jsx');
var NotFoundPage = require('app/components/notFoundPage.jsx');
var ActiveSession = React.createClass({
var ActiveSessionHost = React.createClass({
mixins: [reactor.ReactMixin],
@ -14,18 +16,34 @@ var ActiveSession = React.createClass({
}
},
componentDidMount(){
var { sid } = this.props.params;
if(!this.state.activeSession){
actions.openSession(sid);
}
},
render: function() {
if(!this.state.activeSession){
return null;
}
return <ActiveSession activeSession={this.state.activeSession}/>;
}
});
var ActiveSession = React.createClass({
render: function() {
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>
@ -34,7 +52,7 @@ var ActiveSession = React.createClass({
</ul>
</div>
<div>
<div className="btn-group">
{/*<div className="btn-group">
<span className="btn btn-xs btn-primary">128.0.0.1:8888</span>
<div className="btn-group">
<button data-toggle="dropdown" className="btn btn-default btn-xs dropdown-toggle" aria-expanded="true">
@ -45,9 +63,9 @@ var ActiveSession = React.createClass({
<li><a href="#" target="_blank">Logs</a></li>
</ul>
</div>
</div>
</div>*/}
</div>
<TtyConnection {...this.state.activeSession} />
<TtyConnection {...this.props.activeSession} />
</div>
);
}
@ -66,14 +84,14 @@ var TtyConnection = React.createClass({
},
render() {
let component = new React.Component();
return (
<component>
<div style={{height: '100%'}}>
<TtyTerminal tty={this.tty} cols={this.props.cols} rows={this.props.rows} />
{ this.state.isConnected ? <EventStreamer sid={this.props.sid}/> : null }
</component>
</div>
)
}
});
export {ActiveSession, TtyConnection};
module.exports = {ActiveSession, ActiveSessionHost};

View file

@ -1,20 +1,37 @@
var React = require('react');
var NavLeftBar = require('./navLeftBar');
var cfg = require('app/config');
var actions = require('app/modules/actions');
var {ActiveSession} = require('./activeSession/main.jsx');
var reactor = require('app/reactor');
var {actions, getters} = require('app/modules/app');
var App = React.createClass({
componentDidMount(){
actions.fetchNodesAndSessions();
mixins: [reactor.ReactMixin],
getDataBindings() {
return {
app: getters.appState
}
},
componentWillMount(){
actions.initApp();
this.refreshInterval = setInterval(actions.fetchNodesAndSessions, 3000);
},
componentWillUnmount: function() {
clearInterval(this.refreshInterval);
},
render: function() {
if(this.state.app.isInitializing){
return null;
}
return (
<div className="grv-tlpt">
<NavLeftBar/>
<ActiveSession/>
{this.props.activeSessionHost}
<div className="row">
<nav className="" role="navigation" style={{ marginBottom: 0, float: "right" }}>
<ul className="nav navbar-top-links navbar-right">

View file

@ -3,4 +3,5 @@ module.exports.Login = require('./login.jsx');
module.exports.NewUser = require('./newUser.jsx');
module.exports.Nodes = require('./nodes/main.jsx');
module.exports.Sessions = require('./sessions/main.jsx');
module.exports.ActiveSession = require('./activeSession/main.jsx');
module.exports.ActiveSessionHost = require('./activeSession/main.jsx').ActiveSessionHost;
module.exports.NotFoundPage = require('./notFoundPage.jsx');

View file

@ -3,7 +3,9 @@ var $ = require('jQuery');
var reactor = require('app/reactor');
var LinkedStateMixin = require('react-addons-linked-state-mixin');
var {actions} = require('app/modules/user');
var GoogleAuthInfo = require('./googleAuth');
var GoogleAuthInfo = require('./googleAuthLogo');
var cfg = require('app/config');
var LoginInputForm = React.createClass({
mixins: [LinkedStateMixin],
@ -19,7 +21,7 @@ var LoginInputForm = React.createClass({
onClick: function(e) {
e.preventDefault();
if (this.isValid()) {
actions.login({ ...this.state}, '/web');
this.props.onClick(this.state);
}
},
@ -55,20 +57,29 @@ var Login = React.createClass({
getDataBindings() {
return {
// userRequest: getters.userRequest
}
},
render: function() {
onClick(inputData){
var loc = this.props.location;
var redirect = cfg.routes.app;
if(loc.state && loc.state.redirectTo){
redirect = loc.state.redirectTo;
}
actions.login(inputData, redirect);
},
render() {
var isProcessing = false;//this.state.userRequest.get('isLoading');
var isError = false;//this.state.userRequest.get('isError');
return (
<div className="grv-login text-center">
<div className="grv-logo-tprt"></div>
<div className="grv-content grv-flex">
<div className="grv-flex-column">
<LoginInputForm/>
<LoginInputForm onClick={this.onClick}/>
<GoogleAuthInfo/>
<div className="grv-login-info">
<i className="fa fa-question"></i>

View file

@ -4,7 +4,7 @@ var reactor = require('app/reactor');
var {actions, getters} = require('app/modules/invite');
var userModule = require('app/modules/user');
var LinkedStateMixin = require('react-addons-linked-state-mixin');
var GoogleAuthInfo = require('./googleAuth');
var GoogleAuthInfo = require('./googleAuthLogo');
var InviteInputForm = React.createClass({

View file

@ -3,7 +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 {open} = require('app/modules/activeTerminal/actions');
var {createNewSession} = require('app/modules/activeTerminal/actions');
const TextCell = ({rowIndex, data, columnKey, ...props}) => (
<Cell {...props}>
@ -27,28 +27,33 @@ const LoginCell = ({user, rowIndex, data, ...props}) => {
return <Cell {...props} />;
}
var serverId = data[rowIndex].id;
var $lis = [];
function onNewSessionClick(i){
var login = user.logins[i];
return () => createNewSession(serverId, login);
}
for(var i = 0; i < user.logins.length; i++){
$lis.push(<li key={i}><a href="#" target="_blank" onClick={open.bind(null, data[rowIndex].addr, user.logins[i], undefined)}>{user.logins[i]}</a></li>);
$lis.push(<li key={i}><a onClick={onNewSessionClick(i)}>{user.logins[i]}</a></li>);
}
return (
<Cell {...props}>
<div className="btn-group">
<button type="button" onClick={open.bind(null, data[rowIndex].addr, user.logins[0], undefined)} className="btn btn-sm btn-primary">{user.logins[0]}</button>
<button type="button" onClick={onNewSessionClick(0)} className="btn btn-sm btn-primary">{user.logins[0]}</button>
{
$lis.length > 1 ? (
<div className="btn-group">
<button data-toggle="dropdown" className="btn btn-default btn-sm dropdown-toggle" aria-expanded="true">
<span className="caret"></span>
</button>
<ul className="dropdown-menu">
<li><a href="#" target="_blank">Logs</a></li>
<li><a href="#" target="_blank">Logs</a></li>
</ul>
</div>
): null
[
<button key={0} data-toggle="dropdown" className="btn btn-default btn-sm dropdown-toggle" aria-expanded="true">
<span className="caret"></span>
</button>,
<ul key={1} className="dropdown-menu">
{$lis}
</ul>
] )
: null
}
</div>
</Cell>
@ -65,7 +70,7 @@ var Nodes = React.createClass({
user: userGetters.user
}
},
render: function() {
var data = this.state.nodeRecords;
return (

View file

@ -0,0 +1,19 @@
var React = require('react');
var NotFoundPage = React.createClass({
render() {
return (
<div className="grv-page-notfound">
<div className="grv-logo-tprt">Teleport</div>
<div className="grv-warning"><i className="fa fa-warning"></i> </div>
<h1>Whoops, we cannot find that</h1>
<div>Looks like the page you are looking for isn't here any longer</div>
<div>If you believe this is an error, please contact your organization administrator.</div>
<div className="contact-section">If you believe this is an issue with Teleport, please <a href="https://github.com/gravitational/teleport/issues/new">create a GitHub issue.</a>
</div>
</div>
);
}
})
module.exports = NotFoundPage;

View file

@ -1,5 +1,6 @@
var React = require('react');
var reactor = require('app/reactor');
var { Link } = require('react-router');
var {Table, Column, Cell, TextCell} = require('app/components/table.jsx');
var {getters} = require('app/modules/sessions');
var {open} = require('app/modules/activeTerminal/actions');
@ -19,23 +20,15 @@ const UsersCell = ({ rowIndex, data, ...props }) => {
};
const ButtonCell = ({ rowIndex, data, ...props }) => {
let onClick = () => {
var rowData = data[rowIndex];
var {sid, addr} = rowData
var login = rowData.login;
open(addr, login, sid);
}
var sessionUrl = data[rowIndex].sessionUrl;
return (
<Cell {...props}>
<button onClick={onClick} className="btn btn-info btn-circle" type="button">
<Link to={sessionUrl} className="btn btn-info btn-circle" type="button">
<i className="fa fa-terminal"></i>
</button>
</Link>
<button className="btn btn-info btn-circle" type="button">
<i className="fa fa-play-circle"></i>
</button>
</Cell>
)
}
@ -71,13 +64,13 @@ var SessionList = React.createClass({
}
/>
<Column
columnKey="addr"
columnKey="serverIp"
header={<Cell> Node </Cell> }
cell={<TextCell data={data} /> }
/>
<Column
columnKey="addr"
columnKey="serverId"
header={<Cell> Users </Cell> }
cell={<UsersCell data={data} /> }
/>

View file

@ -34,7 +34,7 @@ var TtyTerminal = React.createClass({
this.term.open(this.refs.container);
this.term.on('data', (data) => this.tty.send(data));
this.resize(this.rows, this.cols);
this.resize(this.cols, this.rows);
this.tty.on('open', ()=> this.term.write(CONNECTED_TXT));
this.tty.on('close', ()=> this.term.write(DISCONNECT_TXT));
@ -57,7 +57,7 @@ var TtyTerminal = React.createClass({
}
if(rows !== this.rows || cols !== this.cols){
this.resize(rows, cols)
this.resize(cols, rows)
}
return false;

View file

@ -8,10 +8,15 @@ let cfg = {
renewTokenPath:'/v1/webapi/sessions/renew',
nodesPath: '/v1/webapi/sites/-current-/nodes',
sessionPath: '/v1/webapi/sessions',
fetchSessionPath: '/v1/webapi/sites/-current-/sessions/:sid',
terminalSessionPath: '/v1/webapi/sites/-current-/sessions/:sid',
invitePath: '/v1/webapi/users/invites/:inviteToken',
createUserPath: '/v1/webapi/users',
getFetchSessionUrl: (sid)=>{
return formatPattern(cfg.api.fetchSessionPath, {sid});
},
getTerminalSessionUrl: (sid)=> {
return formatPattern(cfg.api.terminalSessionPath, {sid});
},
@ -25,9 +30,9 @@ let cfg = {
return `${hostname}/v1/webapi/sites/-current-/sessions/${sid}/events/stream?access_token=${token}`;
},
getTtyConnStr: ({token, addr, login, sid, rows, cols}) => {
getTtyConnStr: ({token, serverId, login, sid, rows, cols}) => {
var params = {
addr,
server_id: serverId,
login,
sid,
term: {
@ -48,11 +53,15 @@ let cfg = {
logout: '/web/logout',
login: '/web/login',
nodes: '/web/nodes',
activeSession: '/web/active-session/:sid',
activeSession: '/web/sessions/:sid',
newUser: '/web/newuser/:inviteToken',
sessions: '/web/sessions'
}
sessions: '/web/sessions',
pageNotFound: '/web/notfound'
},
getActiveSessionRouteUrl(sid){
return formatPattern(cfg.routes.activeSession, {sid});
}
}
export default cfg;

View file

@ -1,7 +1,7 @@
var React = require('react');
var render = require('react-dom').render;
var { Router, Route, Redirect, IndexRoute, browserHistory } = require('react-router');
var { App, Login, Nodes, Sessions, NewUser, ActiveSession } = require('./components');
var { App, Login, Nodes, Sessions, NewUser, ActiveSessionHost, NotFoundPage } = require('./components');
var {ensureUser} = require('./modules/user/actions');
var auth = require('./auth');
var session = require('./session');
@ -21,10 +21,12 @@ render((
<Route path={cfg.routes.login} component={Login}/>
<Route path={cfg.routes.logout} onEnter={handleLogout}/>
<Route path={cfg.routes.newUser} component={NewUser}/>
<Redirect from={cfg.routes.app} to={cfg.routes.nodes}/>
<Route path={cfg.routes.app} component={App} onEnter={ensureUser} >
<IndexRoute component={Nodes}/>
<Route path={cfg.routes.nodes} component={Nodes}/>
<Route path={cfg.routes.activeSession} components={{activeSessionHost: ActiveSessionHost}}/>
<Route path={cfg.routes.sessions} component={Sessions}/>
</Route>
<Route path="*" component={NotFoundPage} />
</Router>
), document.getElementById("app"));

View file

@ -1,30 +0,0 @@
var reactor = require('app/reactor');
var api = require('app/services/api');
var cfg = require('app/config');
var { TLPT_SESSINS_RECEIVE } = require('./sessions/actionTypes');
var { TLPT_NODES_RECEIVE } = require('./nodes/actionTypes');
export default {
fetchNodesAndSessions(){
api.get(cfg.api.nodesPath).done(json=>{
var nodeArray = [];
var sessions = {};
json.nodes.forEach(item=> {
nodeArray.push(item.node);
if(item.sessions){
item.sessions.forEach(item2=>{
sessions[item2.id] = item2;
})
}
});
reactor.batch(() => {
reactor.dispatch(TLPT_NODES_RECEIVE, nodeArray);
reactor.dispatch(TLPT_SESSINS_RECEIVE, sessions);
});
});
}
}

View file

@ -2,7 +2,5 @@ import keyMirror from 'keymirror'
export default keyMirror({
TLPT_TERM_OPEN: null,
TLPT_TERM_CLOSE: null,
TLPT_TERM_CONNECTED: null,
TLPT_TERM_RECEIVE_PARTIES: null
TLPT_TERM_CLOSE: null
})

View file

@ -1,51 +1,76 @@
var reactor = require('app/reactor');
var session = require('app/session');
var {uuid} = require('app/utils');
var api = require('app/services/api');
var cfg = require('app/config');
var invariant = require('invariant');
var getters = require('./getters');
var sessionModule = require('./../sessions');
var { TLPT_TERM_OPEN, TLPT_TERM_CLOSE, TLPT_TERM_CONNECTED, TLPT_TERM_RECEIVE_PARTIES } = require('./actionTypes');
var { TLPT_TERM_OPEN, TLPT_TERM_CLOSE } = require('./actionTypes');
export default {
var actions = {
close(){
let {isNewSession} = reactor.evaluate(getters.activeSession);
reactor.dispatch(TLPT_TERM_CLOSE);
if(isNewSession){
session.getHistory().push(cfg.routes.nodes);
}else{
session.getHistory().push(cfg.routes.sessions);
}
},
resize(w, h){
invariant(w > 5 || h > 5, 'invalid resize parameters');
// some min values
w = w < 5 ? 5 : w;
h = h < 5 ? 5 : h;
let reqData = { terminal_params: { w, h } };
let {sid} = reactor.evaluate(getters.activeSession);
api.put(cfg.api.getTerminalSessionUrl(sid), reqData).done(()=>{
console.log(`resize with ${w} and ${h} - OK`);
}).fail(()=>{
console.log(`failed to resize with ${w} and ${h}`);
api.put(cfg.api.getTerminalSessionUrl(sid), reqData)
.done(()=>{
console.log(`resize with w:${w} and h:${h} - OK`);
})
.fail(()=>{
console.log(`failed to resize with w:${w} and h:${h}`);
})
},
connected(){
reactor.dispatch(TLPT_TERM_CONNECTED);
openSession(sid){
sessionModule.actions.fetchSession(sid)
.done(()=>{
let sView = reactor.evaluate(sessionModule.getters.sessionViewById(sid));
let { serverId, login } = sView;
reactor.dispatch(TLPT_TERM_OPEN, {
serverId,
login,
sid,
isNewSession: false
});
})
.fail(()=>{
session.getHistory().push(cfg.routes.pageNotFound);
})
},
receiveParties(json){
var parties = json.map(item=>{
return {
user: item.user,
lastActive: new Date(item.last_active)
}
})
createNewSession(serverId, login){
var sid = uuid();
var routeUrl = cfg.getActiveSessionRouteUrl(sid);
var history = session.getHistory();
reactor.dispatch(TLPT_TERM_RECEIVE_PARTIES, parties);
},
reactor.dispatch(TLPT_TERM_OPEN, {
serverId,
login,
sid,
isNewSession: true
});
open(addr, login, sid){
let isNew = !sid;
if(isNew){
sid = uuid();
}
reactor.dispatch(TLPT_TERM_OPEN, {addr, login, sid, isNew} );
history.push(routeUrl);
}
}
export default actions;

View file

@ -1,5 +1,5 @@
var { Store, toImmutable } = require('nuclear-js');
var { TLPT_TERM_OPEN, TLPT_TERM_CLOSE, TLPT_TERM_CONNECTED, TLPT_TERM_RECEIVE_PARTIES } = require('./actionTypes');
var { TLPT_TERM_OPEN, TLPT_TERM_CLOSE } = require('./actionTypes');
export default Store({
getInitialState() {
@ -7,30 +7,20 @@ export default Store({
},
initialize() {
this.on(TLPT_TERM_CONNECTED, connected);
this.on(TLPT_TERM_OPEN, setActiveTerminal);
this.on(TLPT_TERM_CLOSE, close);
this.on(TLPT_TERM_RECEIVE_PARTIES, receiveParties);
}
})
function close(){
return toImmutable(null);
}
function receiveParties(state, parties){
return state.set('parties', toImmutable(parties));
}
function setActiveTerminal(state, settings){
return toImmutable({
isConnecting: true,
...settings
function setActiveTerminal(state, {serverId, login, sid, isNewSession} ){
return toImmutable({
serverId,
login,
sid,
isNewSession
});
}
function connected(state){
return state.set('isConnected', true)
.set('isConnecting', false);
}

View file

@ -6,8 +6,10 @@ const activeSession = [
}
let view = {
isNew: activeTerm.get('isNew'),
isNewSession: activeTerm.get('isNewSession'),
notFound: activeTerm.get('notFound'),
addr: activeTerm.get('addr'),
serverId: activeTerm.get('serverId'),
login: activeTerm.get('login'),
sid: activeTerm.get('sid'),
cols: undefined,
@ -15,8 +17,8 @@ const activeSession = [
};
if(sessions.has(view.sid)){
view.cols = sessions.getIn([view.sid, 'terminal_params', 'H']);
view.rows = sessions.getIn([view.sid, 'terminal_params', 'W']);
view.cols = sessions.getIn([view.sid, 'terminal_params', 'w']);
view.rows = sessions.getIn([view.sid, 'terminal_params', 'h']);
}
return view;

View file

@ -0,0 +1,7 @@
import keyMirror from 'keymirror'
export default keyMirror({
TLPT_APP_INIT: null,
TLPT_APP_FAILED: null,
TLPT_APP_READY: null
})

View file

@ -0,0 +1,43 @@
var reactor = require('app/reactor');
var api = require('app/services/api');
var cfg = require('app/config');
var { TLPT_SESSINS_RECEIVE } = require('./../sessions/actionTypes');
var { TLPT_NODES_RECEIVE } = require('./../nodes/actionTypes');
var { TLPT_APP_INIT, TLPT_APP_FAILED, TLPT_APP_READY } = require('./actionTypes');
export default {
initApp() {
reactor.dispatch(TLPT_APP_INIT);
module.exports.fetchNodesAndSessions()
.done(()=>{
reactor.dispatch(TLPT_APP_READY);
})
.fail(()=>{
reactor.dispatch(TLPT_APP_FAILED);
});
},
fetchNodesAndSessions() {
return api.get(cfg.api.nodesPath).done(json => {
var nodeArray = [];
var sessions = {};
json.nodes.forEach(item => {
nodeArray.push(item.node);
if (item.sessions) {
item.sessions.forEach(item2 => {
sessions[item2.id] = item2;
})
}
});
reactor.batch(() => {
reactor.dispatch(TLPT_NODES_RECEIVE, nodeArray);
reactor.dispatch(TLPT_SESSINS_RECEIVE, sessions);
});
});
}
}

View file

@ -0,0 +1,22 @@
var { Store, toImmutable } = require('nuclear-js');
var { TLPT_APP_INIT, TLPT_APP_FAILED, TLPT_APP_READY } = require('./actionTypes');
var initState = toImmutable({
isReady: false,
isInitializing: false,
isFailed: false
});
export default Store({
getInitialState() {
return initState.set('isInitializing', true);
},
initialize() {
this.on(TLPT_APP_INIT, ()=> initState.set('isInitializing', true));
this.on(TLPT_APP_READY,()=> initState.set('isReady', true));
this.on(TLPT_APP_FAILED,()=> initState.set('isFailed', true));
}
})

View file

@ -0,0 +1,5 @@
const appState = [['tlpt'], app=> app.toJS()];
export default {
appState
}

View file

@ -0,0 +1,3 @@
module.exports.getters = require('./getters');
module.exports.actions = require('./actions');
module.exports.appStore = require('./appStore');

View file

@ -1,9 +1,10 @@
var reactor = require('app/reactor');
reactor.registerStores({
'tlpt': require('./app/appStore'),
'tlpt_active_terminal': require('./activeTerminal/activeTermStore'),
'tlpt_user': require('./user/userStore'),
'tlpt_nodes': require('./nodes/nodeStore'),
'tlpt_invite': require('./invite/inviteStore'),
'tlpt_rest_api': require('./restApi/restApiStore'),
'tlpt_sessions': require('./sessions/sessionStore')
'tlpt_sessions': require('./sessions/sessionStore')
});

View file

@ -3,11 +3,13 @@ var {sessionsByServer} = require('./../sessions/getters');
const nodeListView = [ ['tlpt_nodes'], (nodes) =>{
return nodes.map((item)=>{
var addr = item.get('addr');
var sessions = reactor.evaluate(sessionsByServer(addr));
var serverId = item.get('id');
var sessions = reactor.evaluate(sessionsByServer(serverId));
return {
id: serverId,
hostname: item.get('hostname'),
tags: getTags(item),
addr: addr,
addr: item.get('addr'),
sessionCount: sessions.size
}
}).toJS();
@ -44,5 +46,5 @@ function getTags(node){
export default {
nodeListView
nodeListView
}

View file

@ -1,7 +1,19 @@
var reactor = require('app/reactor');
var api = require('app/services/api');
var cfg = require('app/config');
var { TLPT_SESSINS_RECEIVE, TLPT_SESSINS_UPDATE } = require('./actionTypes');
export default {
fetchSession(sid){
return api.get(cfg.api.getFetchSessionUrl(sid)).then(json=>{
if(json && json.session){
reactor.dispatch(TLPT_SESSINS_UPDATE, json.session);
}
});
},
updateSession(json){
reactor.dispatch(TLPT_SESSINS_UPDATE, json);
},

View file

@ -1,25 +1,25 @@
var { toImmutable } = require('nuclear-js');
var reactor = require('app/reactor');
var cfg = require('app/config');
const sessionsByServer = (addr) => [['tlpt_sessions'], (sessions) =>{
const sessionsByServer = (serverId) => [['tlpt_sessions'], (sessions) =>{
return sessions.valueSeq().filter(item=>{
var parties = item.get('parties') || toImmutable([]);
var hasServer = parties.find(item2=> item2.get('server_addr') === addr);
var hasServer = parties.find(item2=> item2.get('server_id') === serverId);
return hasServer;
}).toList();
}]
const sessionsView = [['tlpt_sessions'], (sessions) =>{
return sessions.valueSeq().map(item=>{
var sid = item.get('id');
var parties = reactor.evaluate(partiesBySessionId(sid));
return {
sid: sid,
addr: parties[0].addr,
login: item.get('login'),
parties: parties
}
}).toJS();
return sessions.valueSeq().map(createView).toJS();
}];
const sessionViewById = (sid)=> [['tlpt_sessions', sid], (session)=>{
if(!session){
return null;
}
return createView(session);
}];
const partiesBySessionId = (sid) =>
@ -35,7 +35,8 @@ const partiesBySessionId = (sid) =>
var user = item.get('user');
return {
user: item.get('user'),
addr: item.get('server_addr'),
serverIp: item.get('remote_addr'),
serverId: item.get('server_id'),
isActive: lastActiveUsrName === user
}
}).toJS();
@ -45,8 +46,29 @@ function getLastActiveUser(parties){
return parties.sortBy(item=> new Date(item.get('lastActive'))).first();
}
function createView(session){
var sid = session.get('id');
var serverIp, serverId;
var parties = reactor.evaluate(partiesBySessionId(sid));
if(parties.length > 0){
serverIp = parties[0].serverIp;
serverId = parties[0].serverId;
}
return {
sid: sid,
sessionUrl: cfg.getActiveSessionRouteUrl(sid),
serverIp,
serverId,
login: session.get('login'),
parties: parties
}
}
export default {
partiesBySessionId,
sessionsByServer,
sessionsView
sessionsView,
sessionViewById
}

View file

@ -17,5 +17,5 @@ function updateSession(state, json){
}
function receiveSessions(state, json){
return state.merge(json);
return toImmutable(json);
}

View file

@ -10,7 +10,7 @@ export default {
ensureUser(nextState, replace, cb){
auth.ensureUser()
.done((userData)=> {
.done((userData)=> {
reactor.dispatch(TLPT_RECEIVE_USER, userData.user );
cb();
})

View file

@ -6,6 +6,7 @@
@import "grv-sessions";
@import "grv-nav";
@import "grv-terminal-host";
@import "grv-page-notfound";
.grv {
background-color: white;

View file

@ -9,11 +9,10 @@
> li.active{
border: none;
background: #2f4050;
background-color: #293846;
}
li:hover{
background-color: #2f4050;
li:hover{
}
li:first-child{

View file

@ -0,0 +1,14 @@
.grv-page-notfound{
margin: 0 auto;
max-width: 600px;
text-align: center;
.grv-warning{
font-size: 60px;
}
.contact-section{
margin-top: 20px;
}
}