mirror of
https://github.com/gravitational/teleport
synced 2024-10-21 01:34:01 +00:00
Renames and configs
This commit is contained in:
parent
3ce5afc00b
commit
80e8e1c195
|
@ -1,6 +1,12 @@
|
|||
'use strict';
|
||||
|
||||
var EventsPage = React.createClass({
|
||||
onStartChange: function(e) {
|
||||
console.log(e.value);
|
||||
},
|
||||
onEndChange: function(e) {
|
||||
console.log(e.value);
|
||||
},
|
||||
showEvent: function(e){
|
||||
this.refs.event.show(e);
|
||||
},
|
||||
|
@ -35,9 +41,9 @@ var EventsPage = React.createClass({
|
|||
<div id="page-wrapper" className="gray-bg">
|
||||
<TopNavBar/>
|
||||
<PageHeader icon="fa fa-list" title="Timeline"/>
|
||||
<div className="wrapper wrapper-content animated fadeInRight">
|
||||
<div className="wrapper wrapper-content">
|
||||
<Box colClass="col-lg-8">
|
||||
<EventsBox events={this.state.entries} onShowEvent={this.showEvent}/>
|
||||
<EventsBox events={this.state.entries} onShowEvent={this.showEvent} onStartChange={this.onStartChange} onEndChange={this.onEndChange}/>
|
||||
</Box>
|
||||
</div>
|
||||
<PageFooter/>
|
||||
|
@ -67,6 +73,8 @@ var EventsContainer = React.createClass({
|
|||
keyboardNavigation: false,
|
||||
forceParse: false,
|
||||
autoclose: true
|
||||
}).on('changeDate', function(e){
|
||||
console.log(e)
|
||||
});
|
||||
},
|
||||
render: function() {
|
||||
|
@ -88,9 +96,9 @@ var EventsContainer = React.createClass({
|
|||
<div className="col-lg-4">
|
||||
<div className="form-group" ref="daterange">
|
||||
<div className="input-daterange input-group" id="datepicker">
|
||||
<input type="text" className="input-sm form-control" name="start" value="05/14/2014"/>
|
||||
<input type="text" className="input-sm form-control" name="start" onChange={this.props.onStartChange}/>
|
||||
<span className="input-group-addon">to</span>
|
||||
<input type="text" className="input-sm form-control" name="end" value="05/22/2014"/>
|
||||
<input type="text" className="input-sm form-control" name="end" onChange={this.props.onEndChange}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -33,42 +33,31 @@ var LeftNavBar = React.createClass({
|
|||
}
|
||||
return "";
|
||||
},
|
||||
items: function() {
|
||||
return grv.nav_sections.concat([
|
||||
{icon: "fa fa-key", url: grv.path("keys"), title: "Keys", key: "keys"},
|
||||
{icon: "fa fa-list", url: grv.path("events"), title: "Timeline", key: "events"},
|
||||
{icon: "fa fa-arrows-h", url: grv.path("webtuns"), title: "Web Tunnels", key: "webtuns"},
|
||||
{icon: "fa fa-hdd-o", url: grv.path("servers"), title: "Instances", key: "servers"},
|
||||
{icon: "fa fa-wechat", url: grv.path("sessions"), title: "Sessions", key: "sessions"},
|
||||
]);
|
||||
},
|
||||
render: function(){
|
||||
var self = this;
|
||||
var items = this.items().map(function(i, index){
|
||||
return (<li className={self.className(i.key)}>
|
||||
<a href={i.url}>
|
||||
<i className={i.icon}></i>
|
||||
<span className="nav-label">{i.title}</span>
|
||||
</a>
|
||||
</li>);
|
||||
});
|
||||
return (
|
||||
<nav className="navbar-default navbar-static-side" role="navigation">
|
||||
<div className="sidebar-collapse">
|
||||
<ul className="nav" id="side-menu">
|
||||
<UserBarItem/>
|
||||
<li className={this.className("keys")}>
|
||||
<a href={grv.path("keys")}>
|
||||
<i className="fa fa-key"></i>
|
||||
<span className="nav-label">Keys</span>
|
||||
</a>
|
||||
</li>
|
||||
<li className={this.className("events")}>
|
||||
<a href={grv.path("events")}>
|
||||
<i className="fa fa-list"></i>
|
||||
<span className="nav-label">Timeline</span>
|
||||
</a>
|
||||
</li>
|
||||
<li className={this.className("webtuns")}>
|
||||
<a href={grv.path("webtuns")}>
|
||||
<i className="fa fa-arrows-h"></i>
|
||||
<span className="nav-label">Web Tunnels</span>
|
||||
</a>
|
||||
</li>
|
||||
<li className={this.className("servers")}>
|
||||
<a href={grv.path("servers")}>
|
||||
<i className="fa fa-hdd-o"></i>
|
||||
<span className="nav-label">Servers</span>
|
||||
</a>
|
||||
</li>
|
||||
<li className={this.className("sessions")}>
|
||||
<a href={grv.path("sessions")}>
|
||||
<i className="fa fa-wechat"></i>
|
||||
<span className="nav-label">Sessions</span>
|
||||
</a>
|
||||
</li>
|
||||
{items}
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
|
|
@ -1,236 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var KeysPage = React.createClass({
|
||||
getInitialState: function(){
|
||||
return {keys: []};
|
||||
},
|
||||
componentDidMount: function() {
|
||||
this.reload();
|
||||
},
|
||||
reload: function(){
|
||||
$.ajax({
|
||||
url: this.props.url,
|
||||
dataType: "json",
|
||||
success: function(data) {
|
||||
this.setState({keys: data});
|
||||
}.bind(this),
|
||||
error: function(xhr, status, err) {
|
||||
console.error(this.props.url, status, err.toString());
|
||||
}.bind(this)
|
||||
});
|
||||
},
|
||||
openKeyForm: function(){
|
||||
this.refs.key.open();
|
||||
},
|
||||
addKey: function(key){
|
||||
$.ajax({
|
||||
url: this.props.url,
|
||||
dataType: "json",
|
||||
type: "POST",
|
||||
data: key,
|
||||
success: function(data) {
|
||||
this.refs.key.reset();
|
||||
this.refs.cert.show(atob(data["value"]));
|
||||
this.reload();
|
||||
}.bind(this),
|
||||
error: function(xhr, status, err) {
|
||||
this.refs.key.reset();
|
||||
console.error(this.props.url, status, err.toString());
|
||||
alert(err.toString());
|
||||
}.bind(this)
|
||||
});
|
||||
},
|
||||
deleteKey: function(key) {
|
||||
if (!confirm("Are you sure you want to delete key " + key + " ?")) {
|
||||
return;
|
||||
}
|
||||
$.ajax({
|
||||
url: this.props.url+"/"+key,
|
||||
type: "DELETE",
|
||||
dataType: "json",
|
||||
success: function(data) {
|
||||
this.reload();
|
||||
}.bind(this),
|
||||
error: function(xhr, status, err) {
|
||||
console.error(this.props.url, status, err.toString());
|
||||
}.bind(this)
|
||||
});
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<div id="wrapper">
|
||||
<LeftNavBar current="keys"/>
|
||||
<div id="page-wrapper" className="gray-bg">
|
||||
<TopNavBar/>
|
||||
<PageHeader icon="fa fa-key" title="Keys"/>
|
||||
<div className="wrapper wrapper-content animated fadeInRight">
|
||||
<Box>
|
||||
<KeysBox keys={this.state.keys} onOpenKeyForm={this.openKeyForm} onKeyDelete={this.deleteKey}/>
|
||||
</Box>
|
||||
</div>
|
||||
<PageFooter/>
|
||||
</div>
|
||||
<SignForm ref="key" onAddKey={this.addKey}/>
|
||||
<CertView ref="cert"/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
var KeysBox = React.createClass({
|
||||
render: function() {
|
||||
if (this.props.keys.length == 0) {
|
||||
return (
|
||||
<div className="text-center m-t-lg">
|
||||
<h1>
|
||||
Public SSH Keys.
|
||||
</h1>
|
||||
<small>You have no SSH Keys added. To get an access to cluster,add and sign your public key here.</small><br/><br/>
|
||||
<BootstrapButton className="btn-primary" onClick={this.props.onOpenKeyForm}>
|
||||
<i className="fa fa-check"></i> Add Key
|
||||
</BootstrapButton>
|
||||
</div>);
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<KeysTable keys={this.props.keys} onKeyDelete={this.props.onKeyDelete}/>
|
||||
<BootstrapButton className="btn-primary" onClick={this.props.onOpenKeyForm}>
|
||||
<i className="fa fa-check"></i> Add Key
|
||||
</BootstrapButton>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
})
|
||||
|
||||
var SignForm = React.createClass({
|
||||
open: function() {
|
||||
this.refs.modal.open();
|
||||
},
|
||||
close: function() {
|
||||
this.refs.modal.close();
|
||||
},
|
||||
reset: function() {
|
||||
React.findDOMNode(this.refs.id).value = "";
|
||||
React.findDOMNode(this.refs.key).value = "";
|
||||
this.refs.modal.close();
|
||||
},
|
||||
confirm: function() {
|
||||
var id = React.findDOMNode(this.refs.id).value.trim();
|
||||
var key = React.findDOMNode(this.refs.key).value.trim();
|
||||
if (!id || !key) {
|
||||
alert("ID and Key can not be empty");
|
||||
return;
|
||||
}
|
||||
this.props.onAddKey({id: id, value: key});
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<BootstrapModal
|
||||
icon="fa-laptop"
|
||||
dialogClass="modal-lg"
|
||||
ref="modal"
|
||||
confirm="OK"
|
||||
cancel="Cancel"
|
||||
onCancel={this.reset}
|
||||
onConfirm={this.confirm}
|
||||
title="Add and Sign SSH Key">
|
||||
<div className="form-group">
|
||||
<label>Key ID</label>
|
||||
<input placeholder="Unique ID for the Key" className="form-control" ref="id"/>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>Public Key</label>
|
||||
<textarea placeholder="Paste your public key here" className="form-control" ref="key" rows="8">
|
||||
</textarea>
|
||||
<p>Once submitted, public key will be signed by this cluster certificate authority,
|
||||
and you will get the signed certificate back. Take this certificate and add it
|
||||
alongside to your key in <strong>user-cert.pub</strong>
|
||||
</p>
|
||||
</div>
|
||||
</BootstrapModal>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var CertView = React.createClass({
|
||||
show: function(cert) {
|
||||
React.findDOMNode(this.refs.key).value = cert;
|
||||
this.refs.modal.open();
|
||||
},
|
||||
reset: function() {
|
||||
React.findDOMNode(this.refs.key).value = "";
|
||||
this.refs.modal.close();
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<BootstrapModal
|
||||
icon="fa-laptop"
|
||||
dialogClass="modal-lg"
|
||||
ref="modal"
|
||||
cancel="Close"
|
||||
onCancel={this.reset}
|
||||
title="Signed SSH Certificate">
|
||||
<p>Congratulations! You can find the certificate below.</p>
|
||||
<p><strong>Copy this certificate</strong> (click on the textarea and press "Ctrl-A" and "Ctrl-C")
|
||||
and save it alongside with your public key, for example to the file <strong>username-cert.pub</strong>.
|
||||
</p>
|
||||
<p>This will allow your SSH client can use it to authenticate with the cluster.</p>
|
||||
<div className="form-group">
|
||||
<label>Signed SSH Certificate</label>
|
||||
<textarea className="form-control" ref="key" rows="8" id="signed-ssh-cert-val"></textarea>
|
||||
</div>
|
||||
</BootstrapModal>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var KeysTable = React.createClass({
|
||||
render: function() {
|
||||
var keyDelete = this.props.onKeyDelete
|
||||
var rows = this.props.keys.map(function (key, index) {
|
||||
return (
|
||||
<KeyRow id={key.id} value={key.value} key={index} onKeyDelete={keyDelete}></KeyRow>
|
||||
);
|
||||
});
|
||||
return (
|
||||
<table className="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Key ID</th>
|
||||
<th>Key</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{rows}
|
||||
</tbody>
|
||||
</table>);
|
||||
}
|
||||
});
|
||||
|
||||
var KeyRow = React.createClass({
|
||||
handleDelete: function(e) {
|
||||
e.preventDefault();
|
||||
this.props.onKeyDelete(this.props.id);
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<tr className="key">
|
||||
<td>{this.props.id}</td>
|
||||
<td>{atob(this.props.value).substring(0, 100)}...</td>
|
||||
<td><a href="#" onClick={this.handleDelete}><i className="fa fa-times text-navy"></i></a></td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
React.render(
|
||||
<KeysPage url={grv.path("api", "keys")}/>,
|
||||
document.body
|
||||
);
|
229
assets/static/js/grv/keys.jsx
Normal file
229
assets/static/js/grv/keys.jsx
Normal file
|
@ -0,0 +1,229 @@
|
|||
'use strict';
|
||||
|
||||
var KeysPage = React.createClass({
|
||||
getInitialState: function(){
|
||||
return {keys: []};
|
||||
},
|
||||
componentDidMount: function() {
|
||||
this.reload();
|
||||
},
|
||||
reload: function(){
|
||||
$.ajax({
|
||||
url: this.props.url,
|
||||
dataType: "json",
|
||||
success: function(data) {
|
||||
this.setState({keys: data});
|
||||
}.bind(this),
|
||||
error: function(xhr, status, err) {
|
||||
console.error(this.props.url, status, err.toString());
|
||||
}.bind(this)
|
||||
});
|
||||
},
|
||||
openKeyForm: function(){
|
||||
this.refs.key.open();
|
||||
},
|
||||
addKey: function(key){
|
||||
$.ajax({
|
||||
url: this.props.url,
|
||||
dataType: "json",
|
||||
type: "POST",
|
||||
data: key,
|
||||
success: function(data) {
|
||||
this.refs.key.reset();
|
||||
this.refs.cert.show(atob(data["value"]));
|
||||
this.reload();
|
||||
}.bind(this),
|
||||
error: function(xhr, status, err) {
|
||||
this.refs.key.reset();
|
||||
toastr.error(err.toString());
|
||||
console.error(this.props.url, status, err.toString());
|
||||
}.bind(this)
|
||||
});
|
||||
},
|
||||
deleteKey: function(key) {
|
||||
if (!confirm("Are you sure you want to delete key " + key + " ?")) {
|
||||
return;
|
||||
}
|
||||
$.ajax({
|
||||
url: this.props.url+"/"+key,
|
||||
type: "DELETE",
|
||||
dataType: "json",
|
||||
success: function(data) {
|
||||
this.reload();
|
||||
}.bind(this),
|
||||
error: function(xhr, status, err) {
|
||||
console.error(this.props.url, status, err.toString());
|
||||
toastr.error(err.toString());
|
||||
}.bind(this)
|
||||
});
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<div id="wrapper">
|
||||
<LeftNavBar current="keys"/>
|
||||
<div id="page-wrapper" className="gray-bg">
|
||||
<TopNavBar/>
|
||||
<PageHeader icon="fa fa-key" title="Keys"/>
|
||||
<div className="wrapper wrapper-content">
|
||||
<Box>
|
||||
<KeysBox keys={this.state.keys} onOpenKeyForm={this.openKeyForm} onKeyDelete={this.deleteKey}/>
|
||||
</Box>
|
||||
</div>
|
||||
<PageFooter/>
|
||||
</div>
|
||||
<SignForm ref="key" onAddKey={this.addKey}/>
|
||||
<CertView ref="cert"/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var KeysBox = React.createClass({
|
||||
render: function() {
|
||||
if (this.props.keys.length == 0) {
|
||||
return (
|
||||
<div className="text-center m-t-lg">
|
||||
<h1>
|
||||
Public SSH Keys.
|
||||
</h1>
|
||||
<small>You have no SSH Keys added. To get an access to cluster,add and sign your public key here.</small><br/><br/>
|
||||
<BootstrapButton className="btn-primary" onClick={this.props.onOpenKeyForm}>
|
||||
<i className="fa fa-check"></i> Add Key
|
||||
</BootstrapButton>
|
||||
</div>);
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<KeysTable keys={this.props.keys} onKeyDelete={this.props.onKeyDelete}/>
|
||||
<BootstrapButton className="btn-primary" onClick={this.props.onOpenKeyForm}>
|
||||
<i className="fa fa-check"></i> Add Key
|
||||
</BootstrapButton>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
})
|
||||
|
||||
var SignForm = React.createClass({
|
||||
open: function() {
|
||||
this.refs.modal.open();
|
||||
},
|
||||
close: function() {
|
||||
this.refs.modal.close();
|
||||
},
|
||||
reset: function() {
|
||||
React.findDOMNode(this.refs.id).value = "";
|
||||
React.findDOMNode(this.refs.key).value = "";
|
||||
this.refs.modal.close();
|
||||
},
|
||||
confirm: function() {
|
||||
var id = React.findDOMNode(this.refs.id).value.trim();
|
||||
var key = React.findDOMNode(this.refs.key).value.trim();
|
||||
if (!id || !key) {
|
||||
alert("ID and Key can not be empty");
|
||||
return;
|
||||
}
|
||||
this.props.onAddKey({id: id, value: key});
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<BootstrapModal icon="fa fa-key"
|
||||
dialogClass="modal-lg"
|
||||
ref="modal"
|
||||
confirm="OK"
|
||||
cancel="Cancel"
|
||||
onCancel={this.reset}
|
||||
onConfirm={this.confirm}
|
||||
title="Add SSH Key">
|
||||
<div className="form-group">
|
||||
<label>Key ID</label>
|
||||
<input placeholder="Unique ID for the Key" className="form-control" ref="id"/>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>Public Key</label>
|
||||
<textarea placeholder="Paste your public key here" className="form-control" ref="key" rows="8">
|
||||
</textarea>
|
||||
<p>Once submitted, public key will be signed by this cluster certificate authority,
|
||||
and you will get the signed certificate back. Take this certificate and add it
|
||||
alongside to your key in <strong>user-cert.pub</strong>
|
||||
</p>
|
||||
</div>
|
||||
</BootstrapModal>);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var CertView = React.createClass({
|
||||
show: function(cert) {
|
||||
React.findDOMNode(this.refs.key).value = cert;
|
||||
this.refs.modal.open();
|
||||
},
|
||||
reset: function() {
|
||||
React.findDOMNode(this.refs.key).value = "";
|
||||
this.refs.modal.close();
|
||||
},
|
||||
render: function() {
|
||||
return (<BootstrapModal icon="fa fa-key"
|
||||
dialogClass="modal-lg"
|
||||
ref="modal"
|
||||
cancel="Close"
|
||||
onCancel={this.reset}
|
||||
title="Signed SSH Certificate">
|
||||
<p>Congratulations! You can find the certificate below.</p>
|
||||
<p><strong>Copy this certificate</strong> (click on the textarea and press "Ctrl-A" and "Ctrl-C")
|
||||
and save it alongside with your public key, for example to the file <strong>username-cert.pub</strong>.
|
||||
</p>
|
||||
<p>This will allow your SSH client can use it to authenticate with the cluster.</p>
|
||||
<div className="form-group">
|
||||
<label>Signed SSH Certificate</label>
|
||||
<textarea className="form-control" ref="key" rows="8" id="signed-ssh-cert-val"></textarea>
|
||||
</div>
|
||||
</BootstrapModal>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var KeysTable = React.createClass({
|
||||
render: function() {
|
||||
var keyDelete = this.props.onKeyDelete
|
||||
var rows = this.props.keys.map(function (key, index) {
|
||||
return (
|
||||
<KeyRow id={key.id} value={key.value} key={index} onKeyDelete={keyDelete}></KeyRow>
|
||||
);
|
||||
});
|
||||
return (
|
||||
<table className="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Key ID</th>
|
||||
<th>Key</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{rows}
|
||||
</tbody>
|
||||
</table>);
|
||||
}
|
||||
});
|
||||
|
||||
var KeyRow = React.createClass({
|
||||
handleDelete: function(e) {
|
||||
e.preventDefault();
|
||||
this.props.onKeyDelete(this.props.id);
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<tr className="key">
|
||||
<td>{this.props.id}</td>
|
||||
<td>{atob(this.props.value).substring(0, 100)}...</td>
|
||||
<td><a href="#" onClick={this.handleDelete}><i className="fa fa-times text-navy"></i></a></td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
React.render(
|
||||
<KeysPage url={grv.path("api", "keys")}/>,
|
||||
document.body
|
||||
);
|
|
@ -36,7 +36,7 @@ var ServersPage = React.createClass({
|
|||
<div id="page-wrapper" className="gray-bg">
|
||||
<TopNavBar/>
|
||||
<PageHeader title="SSH Servers" icon="fa fa-hdd-o"/>
|
||||
<div className="wrapper wrapper-content animated fadeInRight">
|
||||
<div className="wrapper wrapper-content">
|
||||
<Box>
|
||||
<ServersTable servers={this.state.servers} onConnect={this.connect}/>
|
||||
</Box>
|
||||
|
|
|
@ -52,7 +52,7 @@ var SessionPage = React.createClass({
|
|||
<LeftNavBar current="sessions"/>
|
||||
<div id="page-wrapper" className="gray-bg">
|
||||
<TopNavBar/>
|
||||
<div className="wrapper wrapper-content animated fadeInRight">
|
||||
<div className="wrapper wrapper-content">
|
||||
<div className="row">
|
||||
<div className="col-lg-9" style={{width: '920px'}}>
|
||||
<ConsoleBox session={this.state.session}
|
||||
|
|
|
@ -30,7 +30,7 @@ var SessionsPage = React.createClass({
|
|||
<div id="page-wrapper" className="gray-bg">
|
||||
<TopNavBar/>
|
||||
<PageHeader title="Active Sessions" icon="fa fa-wechat"/>
|
||||
<div className="wrapper wrapper-content animated fadeInRight">
|
||||
<div className="wrapper wrapper-content">
|
||||
<Box>
|
||||
<SessionsTable sessions={this.state.sessions}/>
|
||||
</Box>
|
||||
|
|
|
@ -62,7 +62,7 @@ var WebTunsPage = React.createClass({
|
|||
<div id="page-wrapper" className="gray-bg">
|
||||
<TopNavBar/>
|
||||
<PageHeader title="Web Tunnels" icon="fa fa-arrows-h"/>
|
||||
<div className="wrapper wrapper-content animated fadeInRight">
|
||||
<div className="wrapper wrapper-content">
|
||||
<Box>
|
||||
<WebTunsBox tuns={this.state.tuns} onOpenTunForm={this.openTunForm} onTunDelete={this.deleteTun}/>
|
||||
</Box>
|
||||
|
|
|
@ -48,14 +48,15 @@
|
|||
<!-- Gravity stuff -->
|
||||
<script type="text/javascript">
|
||||
grv = {
|
||||
prefix: "{{.Prefix}}",
|
||||
prefix: "{{.Cfg.URLPrefix}}",
|
||||
path: function() {
|
||||
var path = ["{{.Prefix}}" != ""? "{{.Prefix}}":""];
|
||||
var path = ["{{.Cfg.URLPrefix}}" != ""? "{{.Cfg.URLPrefix}}":""];
|
||||
for(var i = 0; i < arguments.length; i++) {
|
||||
path.push(arguments[i]);
|
||||
}
|
||||
return path.join("/");
|
||||
}
|
||||
},
|
||||
nav_sections: {{.Cfg.NavSections}}
|
||||
};
|
||||
</script>
|
||||
<script src="{{Path "/static/js/grv/lib.js"}}"></script>
|
||||
|
|
|
@ -9,5 +9,5 @@
|
|||
{{ end }}
|
||||
|
||||
{{ define "script" }}
|
||||
<script type="text/jsx" src="{{Path "/static/js/grv/keys.js"}}"></script>
|
||||
<script type="text/jsx" src="{{Path "/static/js/grv/keys.jsx"}}"></script>
|
||||
{{ end }}
|
||||
|
|
41
cp/cp.go
41
cp/cp.go
|
@ -29,17 +29,32 @@ import (
|
|||
// CPHandler implements methods for control panel
|
||||
type CPHandler struct {
|
||||
httprouter.Router
|
||||
auth AuthHandler
|
||||
prefix string
|
||||
cfg HandlerConfig
|
||||
}
|
||||
|
||||
func NewCPHandler(auth AuthHandler, assetsDir, prefix string) *CPHandler {
|
||||
type HandlerConfig struct {
|
||||
Auth AuthHandler
|
||||
AssetsDir string
|
||||
NavSections []NavSection
|
||||
URLPrefix string
|
||||
}
|
||||
|
||||
type NavSection struct {
|
||||
Title string `json:"title"`
|
||||
URL string `json:"url"`
|
||||
Icon string `json:"icon"`
|
||||
}
|
||||
|
||||
func NewCPHandler(cfg HandlerConfig) *CPHandler {
|
||||
if len(cfg.NavSections) == 0 {
|
||||
// to avoid panics during iterations
|
||||
cfg.NavSections = []NavSection{}
|
||||
}
|
||||
h := &CPHandler{
|
||||
auth: auth,
|
||||
prefix: prefix,
|
||||
cfg: cfg,
|
||||
}
|
||||
|
||||
h.initTemplates(assetsDir)
|
||||
h.initTemplates(cfg.AssetsDir)
|
||||
h.GET("/login", h.login)
|
||||
h.GET("/logout", h.logout)
|
||||
h.POST("/auth", h.authForm)
|
||||
|
@ -83,7 +98,7 @@ func NewCPHandler(auth AuthHandler, assetsDir, prefix string) *CPHandler {
|
|||
|
||||
// Static assets
|
||||
h.Handler("GET", "/static/*filepath",
|
||||
http.FileServer(http.Dir(filepath.Join(assetsDir, "assets"))))
|
||||
http.FileServer(http.Dir(filepath.Join(cfg.AssetsDir, "assets"))))
|
||||
|
||||
// Operations with sessions
|
||||
h.GET("/api/sessions", h.needsAuth(h.getSessions))
|
||||
|
@ -109,7 +124,7 @@ func (s *CPHandler) needsAuth(fn RequestHandler) httprouter.Handle {
|
|||
http.Redirect(w, r, "/login", http.StatusFound)
|
||||
return
|
||||
}
|
||||
ctx, err := s.auth.ValidateSession(d.User, d.SID)
|
||||
ctx, err := s.cfg.Auth.ValidateSession(d.User, d.SID)
|
||||
if err != nil {
|
||||
log.Warningf("failed to validate session: %v", err)
|
||||
http.Redirect(w, r, "/login", http.StatusFound)
|
||||
|
@ -238,7 +253,7 @@ func (s *CPHandler) downloadFiles(w http.ResponseWriter, r *http.Request, p http
|
|||
}
|
||||
|
||||
ck := &http.Cookie{
|
||||
Domain: fmt.Sprintf(".%v", s.auth.GetHost()),
|
||||
Domain: fmt.Sprintf(".%v", s.cfg.Auth.GetHost()),
|
||||
Name: "fileDownload",
|
||||
Value: "true",
|
||||
Path: "/",
|
||||
|
@ -624,7 +639,7 @@ func (s *CPHandler) login(w http.ResponseWriter, r *http.Request, _ httprouter.P
|
|||
}
|
||||
|
||||
func (s *CPHandler) logout(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
s.auth.ClearSession(w)
|
||||
s.cfg.Auth.ClearSession(w)
|
||||
http.Redirect(w, r, s.Path("login"), http.StatusFound)
|
||||
}
|
||||
|
||||
|
@ -639,13 +654,13 @@ func (s *CPHandler) authForm(w http.ResponseWriter, r *http.Request, p httproute
|
|||
replyErr(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
sid, err := s.auth.Auth(user, pass)
|
||||
sid, err := s.cfg.Auth.Auth(user, pass)
|
||||
if err != nil {
|
||||
log.Warningf("auth error: %v", err)
|
||||
http.Redirect(w, r, "/login", http.StatusFound)
|
||||
return
|
||||
}
|
||||
if err := s.auth.SetSession(w, user, sid); err != nil {
|
||||
if err := s.cfg.Auth.SetSession(w, user, sid); err != nil {
|
||||
replyErr(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
@ -656,7 +671,7 @@ func (s *CPHandler) executeTemplate(w http.ResponseWriter, name string, data map
|
|||
if data == nil {
|
||||
data = map[string]interface{}{}
|
||||
}
|
||||
data["Prefix"] = s.prefix
|
||||
data["Cfg"] = s.cfg
|
||||
tpl, ok := templates[name]
|
||||
if !ok {
|
||||
replyErr(w, http.StatusInternalServerError, fmt.Errorf("template '%v' not found", name))
|
||||
|
|
|
@ -30,7 +30,7 @@ func NewServer(cfg Config) (*CPServer, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cp := NewCPHandler(auth, cfg.AssetsDir, "")
|
||||
cp := NewCPHandler(HandlerConfig{Auth: auth, AssetsDir: cfg.AssetsDir})
|
||||
proxy := newProxyHandler(cp, cfg.AuthSrv, cfg.Host)
|
||||
return &CPServer{
|
||||
cfg: cfg,
|
||||
|
|
Loading…
Reference in a new issue