mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-12 12:33:02 +00:00
Handle named servers in UserSpawnHandler, home, admin pages
Made CSS and HTML (and Jinja2) of admin page compatible with named servers.
This commit is contained in:
@@ -20,7 +20,7 @@ from sqlalchemy.exc import SQLAlchemyError
|
||||
from tornado.log import app_log
|
||||
from tornado.httputil import url_concat, HTTPHeaders
|
||||
from tornado.ioloop import IOLoop
|
||||
from tornado.web import RequestHandler
|
||||
from tornado.web import RequestHandler, MissingArgumentError
|
||||
from tornado import gen, web
|
||||
|
||||
from .. import __version__
|
||||
@@ -854,10 +854,10 @@ class BaseHandler(RequestHandler):
|
||||
await self.proxy.delete_user(user, server_name)
|
||||
await user.stop(server_name)
|
||||
|
||||
async def stop_single_user(self, user, name=''):
|
||||
if name not in user.spawners:
|
||||
raise KeyError("User %s has no such spawner %r", user.name, name)
|
||||
spawner = user.spawners[name]
|
||||
async def stop_single_user(self, user, server_name=''):
|
||||
if server_name not in user.spawners:
|
||||
raise KeyError("User %s has no such spawner %r", user.name, server_name)
|
||||
spawner = user.spawners[server_name]
|
||||
if spawner.pending:
|
||||
raise RuntimeError("%s pending %s" % (spawner._log_name, spawner.pending))
|
||||
# set user._stop_pending before doing anything async
|
||||
@@ -873,8 +873,8 @@ class BaseHandler(RequestHandler):
|
||||
"""
|
||||
tic = IOLoop.current().time()
|
||||
try:
|
||||
await self.proxy.delete_user(user, name)
|
||||
await user.stop(name)
|
||||
await self.proxy.delete_user(user, server_name)
|
||||
await user.stop(server_name)
|
||||
finally:
|
||||
spawner._stop_pending = False
|
||||
toc = IOLoop.current().time()
|
||||
@@ -885,7 +885,7 @@ class BaseHandler(RequestHandler):
|
||||
await gen.with_timeout(timedelta(seconds=self.slow_stop_timeout), stop())
|
||||
except gen.TimeoutError:
|
||||
# hit timeout, but stop is still pending
|
||||
self.log.warning("User %s:%s server is slow to stop", user.name, name)
|
||||
self.log.warning("User %s:%s server is slow to stop", user.name, server_name)
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# template rendering
|
||||
@@ -1019,7 +1019,7 @@ class PrefixRedirectHandler(BaseHandler):
|
||||
|
||||
|
||||
class UserSpawnHandler(BaseHandler):
|
||||
"""Redirect requests to /user/name/* handled by the Hub.
|
||||
"""Redirect requests to /user/user_name/* handled by the Hub.
|
||||
|
||||
If logged in, spawn a single-user server and redirect request.
|
||||
If a user, alice, requests /user/bob/notebooks/mynotebook.ipynb,
|
||||
@@ -1035,21 +1035,21 @@ class UserSpawnHandler(BaseHandler):
|
||||
self.write(json.dumps({"message": "%s is not running" % user.name}))
|
||||
self.finish()
|
||||
|
||||
async def get(self, name, user_path):
|
||||
async def get(self, user_name, user_path):
|
||||
if not user_path:
|
||||
user_path = '/'
|
||||
current_user = self.current_user
|
||||
if (
|
||||
current_user
|
||||
and current_user.name != name
|
||||
and current_user.name != user_name
|
||||
and current_user.admin
|
||||
and self.settings.get('admin_access', False)
|
||||
):
|
||||
# allow admins to spawn on behalf of users
|
||||
user = self.find_user(name)
|
||||
user = self.find_user(user_name)
|
||||
if user is None:
|
||||
# no such user
|
||||
raise web.HTTPError(404, "No such user %s" % name)
|
||||
raise web.HTTPError(404, "No such user %s" % user_name)
|
||||
self.log.info("Admin %s requesting spawn on behalf of %s",
|
||||
current_user.name, user.name)
|
||||
admin_spawn = True
|
||||
@@ -1059,7 +1059,7 @@ class UserSpawnHandler(BaseHandler):
|
||||
admin_spawn = False
|
||||
# For non-admins, we should spawn if the user matches
|
||||
# otherwise redirect users to their own server
|
||||
should_spawn = (current_user and current_user.name == name)
|
||||
should_spawn = (current_user and current_user.name == user_name)
|
||||
|
||||
if "api" in user_path.split("/") and not user.active:
|
||||
# API request for not-running server (e.g. notebook UI left open)
|
||||
@@ -1071,7 +1071,7 @@ class UserSpawnHandler(BaseHandler):
|
||||
# if spawning fails for any reason, point users to /hub/home to retry
|
||||
self.extra_error_html = self.spawn_home_error
|
||||
|
||||
# If people visit /user/:name directly on the Hub,
|
||||
# If people visit /user/:user_name directly on the Hub,
|
||||
# the redirects will just loop, because the proxy is bypassed.
|
||||
# Try to check for that and warn,
|
||||
# though the user-facing behavior is unchanged
|
||||
@@ -1087,7 +1087,11 @@ class UserSpawnHandler(BaseHandler):
|
||||
""", self.request.full_url(), self.proxy.public_url)
|
||||
|
||||
# logged in as valid user, check for pending spawn
|
||||
spawner = user.spawner
|
||||
if self.allow_named_servers:
|
||||
server_name = self.get_argument('server', '')
|
||||
else:
|
||||
server_name = ''
|
||||
spawner = user.spawners[server_name]
|
||||
|
||||
# First, check for previous failure.
|
||||
if (
|
||||
@@ -1152,7 +1156,7 @@ class UserSpawnHandler(BaseHandler):
|
||||
{'next': self.request.uri}))
|
||||
return
|
||||
else:
|
||||
await self.spawn_single_user(user)
|
||||
await self.spawn_single_user(user, server_name)
|
||||
|
||||
# spawn didn't finish, show pending page
|
||||
if spawner.pending:
|
||||
@@ -1208,7 +1212,7 @@ class UserSpawnHandler(BaseHandler):
|
||||
url_parts = urlparse(target)
|
||||
query_parts = parse_qs(url_parts.query)
|
||||
query_parts['redirects'] = redirects + 1
|
||||
url_parts = url_parts._replace(query=urlencode(query_parts))
|
||||
url_parts = url_parts._replace(query=urlencode(query_parts, doseq=True))
|
||||
target = urlunparse(url_parts)
|
||||
else:
|
||||
target = url_concat(target, {'redirects': 1})
|
||||
@@ -1276,7 +1280,7 @@ class AddSlashHandler(BaseHandler):
|
||||
|
||||
default_handlers = [
|
||||
(r'', AddSlashHandler), # add trailing / to `/hub`
|
||||
(r'/user/([^/]+)(/.*)?', UserSpawnHandler),
|
||||
(r'/user/(?P<user_name>[^/]+)(?P<user_path>/.*)?', UserSpawnHandler),
|
||||
(r'/user-redirect/(.*)?', UserRedirectHandler),
|
||||
(r'/security/csp-report', CSPReportHandler),
|
||||
]
|
||||
|
@@ -50,13 +50,18 @@ class HomeHandler(BaseHandler):
|
||||
# trigger poll_and_notify event in case of a server that died
|
||||
await user.spawner.poll_and_notify()
|
||||
|
||||
# send the user to /spawn if they aren't running or pending a spawn,
|
||||
# send the user to /spawn if they have no active servers,
|
||||
# to establish that this is an explicit spawn request rather
|
||||
# than an implicit one, which can be caused by any link to `/user/:name`
|
||||
url = user.url if user.spawner.active else url_path_join(self.hub.base_url, 'spawn')
|
||||
# than an implicit one, which can be caused by any link to `/user/:name(/:server_name)`
|
||||
url = url_path_join(self.hub.base_url, 'user', user.name) if user.active else url_path_join(self.hub.base_url, 'spawn')
|
||||
html = self.render_template('home.html',
|
||||
user=user,
|
||||
url=url,
|
||||
allow_named_servers=self.allow_named_servers,
|
||||
url_path_join=url_path_join,
|
||||
# can't use user.spawners because the stop method of User pops named servers from user.spawners when they're stopped
|
||||
spawners = user.orm_user._orm_spawners,
|
||||
default_server = user.spawner,
|
||||
)
|
||||
self.finish(html)
|
||||
|
||||
@@ -214,11 +219,12 @@ class AdminHandler(BaseHandler):
|
||||
running = [ u for u in users if u.running ]
|
||||
|
||||
html = self.render_template('admin.html',
|
||||
user=self.current_user,
|
||||
current_user=self.current_user,
|
||||
admin_access=self.settings.get('admin_access', False),
|
||||
users=users,
|
||||
running=running,
|
||||
sort={s:o for s,o in zip(sorts, orders)},
|
||||
allow_named_servers=self.allow_named_servers,
|
||||
)
|
||||
self.finish(html)
|
||||
|
||||
|
@@ -182,19 +182,19 @@ class User:
|
||||
await self.save_auth_state(auth_state)
|
||||
return auth_state
|
||||
|
||||
def _new_spawner(self, name, spawner_class=None, **kwargs):
|
||||
def _new_spawner(self, server_name, spawner_class=None, **kwargs):
|
||||
"""Create a new spawner"""
|
||||
if spawner_class is None:
|
||||
spawner_class = self.spawner_class
|
||||
self.log.debug("Creating %s for %s:%s", spawner_class, self.name, name)
|
||||
self.log.debug("Creating %s for %s:%s", spawner_class, self.name, server_name)
|
||||
|
||||
orm_spawner = self.orm_spawners.get(name)
|
||||
orm_spawner = self.orm_spawners.get(server_name)
|
||||
if orm_spawner is None:
|
||||
orm_spawner = orm.Spawner(user=self.orm_user, name=name)
|
||||
orm_spawner = orm.Spawner(user=self.orm_user, name=server_name)
|
||||
self.db.add(orm_spawner)
|
||||
self.db.commit()
|
||||
assert name in self.orm_spawners
|
||||
if name == '' and self.state:
|
||||
assert server_name in self.orm_spawners
|
||||
if server_name == '' and self.state:
|
||||
# migrate user.state to spawner.state
|
||||
orm_spawner.state = self.state
|
||||
self.state = None
|
||||
@@ -202,15 +202,15 @@ class User:
|
||||
# use fully quoted name for client_id because it will be used in cookie-name
|
||||
# self.escaped_name may contain @ which is legal in URLs but not cookie keys
|
||||
client_id = 'jupyterhub-user-%s' % quote(self.name)
|
||||
if name:
|
||||
client_id = '%s-%s' % (client_id, quote(name))
|
||||
if server_name:
|
||||
client_id = '%s-%s' % (client_id, quote(server_name))
|
||||
spawn_kwargs = dict(
|
||||
user=self,
|
||||
orm_spawner=orm_spawner,
|
||||
hub=self.settings.get('hub'),
|
||||
authenticator=self.authenticator,
|
||||
config=self.settings.get('config'),
|
||||
proxy_spec=url_path_join(self.proxy_spec, name, '/'),
|
||||
proxy_spec=url_path_join(self.proxy_spec, server_name, '/'),
|
||||
db=self.db,
|
||||
oauth_client_id=client_id,
|
||||
cookie_options = self.settings.get('cookie_options', {}),
|
||||
|
@@ -62,24 +62,50 @@ require(["jquery", "bootstrap", "moment", "jhapi", "utils"], function ($, bs, mo
|
||||
el.text(m.isValid() ? m.fromNow() : "Never");
|
||||
});
|
||||
|
||||
$(".stop-server").click(function () {
|
||||
$(".stop-server.default-server").click(function () {
|
||||
var el = $(this);
|
||||
var row = get_row(el);
|
||||
var user = row.data('user');
|
||||
el.text("stopping...");
|
||||
api.stop_server(user, {
|
||||
success: function () {
|
||||
el.text('stop server').addClass('hidden');
|
||||
row.find('.access-server').addClass('hidden');
|
||||
row.find('.start-server').removeClass('hidden');
|
||||
}
|
||||
if (el.data("named_servers") === true) {
|
||||
el.text("stop default server").addClass("hidden");
|
||||
} else {
|
||||
el.text("stop server").addClass("hidden");
|
||||
}
|
||||
row.find(".access-server").addClass("hidden");
|
||||
row.find(".start-server").removeClass("hidden");
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
$(".access-server").map(function (i, el) {
|
||||
$(".stop-server[id^='stop-']").click(function () {
|
||||
var el = $(this);
|
||||
var row = get_row(el);
|
||||
var user = row.data("user");
|
||||
var server_name = (this.id).replace(/^(stop-)/, "");
|
||||
el.text("stopping...");
|
||||
api.stop_named_server(user, server_name, {
|
||||
success: function() {
|
||||
el.text("stop "+server_name).addClass("hidden");
|
||||
row.find(".access-server").addClass("hidden");
|
||||
row.find(".start-server").removeClass("hidden");
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
$(".access-server.default-server").map(function (i, el) {
|
||||
el = $(el);
|
||||
var user = get_row(el).data('user');
|
||||
el.attr('href', utils.url_path_join(prefix, 'user', user) + '/');
|
||||
var user = get_row(el).data("user");
|
||||
el.attr("href", utils.url_path_join(prefix, "user", user) + "/");
|
||||
});
|
||||
|
||||
$(".access-server[id^='access-']").map(function (i, el) {
|
||||
el = $(el);
|
||||
var user = get_row(el).data("user");
|
||||
var server_name = (this.id).replace(/^(access-)/, "");
|
||||
el.attr("href", utils.url_path_join(prefix, "user", user, server_name) + "/" );
|
||||
});
|
||||
|
||||
if (admin_access && options_form) {
|
||||
@@ -94,16 +120,35 @@ require(["jquery", "bootstrap", "moment", "jhapi", "utils"], function ($, bs, mo
|
||||
// since it would mean opening a bunch of tabs
|
||||
$('#start-all-servers').addClass('hidden');
|
||||
} else {
|
||||
$(".start-server").click(function () {
|
||||
$(".start-server.default-server").click(function () {
|
||||
var el = $(this);
|
||||
var row = get_row(el);
|
||||
var user = row.data('user');
|
||||
el.text("starting...");
|
||||
api.start_server(user, {
|
||||
success: function () {
|
||||
el.text('start server').addClass('hidden');
|
||||
row.find('.stop-server').removeClass('hidden');
|
||||
row.find('.access-server').removeClass('hidden');
|
||||
if (el.data("named_servers") === true ){
|
||||
el.text("start default server").addClass("hidden");
|
||||
} else {
|
||||
el.text("start server").addClass("hidden");
|
||||
}
|
||||
row.find(".stop-server").removeClass("hidden");
|
||||
row.find(".access-server").removeClass("hidden");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$(".start-server[id^='start-']").click(function () {
|
||||
var el = $(this);
|
||||
var row = get_row(el);
|
||||
var user = row.data("user");
|
||||
var server_name = (this.id).replace(/^(start-)/, "");
|
||||
el.text("starting...");
|
||||
api.start_named_server(user, server_name, {
|
||||
success: function () {
|
||||
el.text("start "+server_name).addClass("hidden");
|
||||
row.find(".stop-server").removeClass("hidden");
|
||||
row.find(".access-server").removeClass("hidden");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@@ -17,13 +17,156 @@ require(["jquery", "jhapi"], function($, JHAPI) {
|
||||
});
|
||||
api.stop_server(user, {
|
||||
success: function() {
|
||||
if ($("#start").data("named_servers") === true) {
|
||||
$("#start")
|
||||
.text("Start Default Server")
|
||||
.attr("title", "Start the default server")
|
||||
.attr("disabled", false)
|
||||
.off("click");
|
||||
} else {
|
||||
$("#start")
|
||||
.text("Start My Server")
|
||||
.attr("title", "Start your server")
|
||||
.attr("disabled", false)
|
||||
.off("click");
|
||||
$("#stop").hide();
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
//// Named servers buttons
|
||||
|
||||
function stop() {
|
||||
let server_name = (this.id).replace(/^(stop-)/, "");
|
||||
// before request
|
||||
$("#stop-"+server_name)
|
||||
.attr("disabled", true)
|
||||
.off("click")
|
||||
.click(function() {
|
||||
return false;});
|
||||
|
||||
$("#goto-"+server_name)
|
||||
.attr("disabled", true)
|
||||
.off("click")
|
||||
.click(function() {
|
||||
return false;});
|
||||
|
||||
// request
|
||||
api.stop_named_server(user, server_name, {
|
||||
success: function() {
|
||||
// after request --> establish final local state
|
||||
$("#stop-"+server_name)
|
||||
.data("state", false)
|
||||
.hide()
|
||||
|
||||
$("#goto-"+server_name)
|
||||
.data("state", false)
|
||||
.hide()
|
||||
|
||||
$("#start-"+server_name)
|
||||
.data("state", false)
|
||||
.show()
|
||||
.attr("disabled", false)
|
||||
.off("click")
|
||||
.click(start);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
function goto() {
|
||||
let server_name = (this.id).replace(/^(goto-)/, "");
|
||||
// before request
|
||||
$("#stop-"+server_name)
|
||||
.attr("disabled", true)
|
||||
.off("click")
|
||||
.click(function() {
|
||||
return false;});
|
||||
|
||||
$("#goto-"+server_name)
|
||||
.attr("disabled", true)
|
||||
.off("click")
|
||||
.click(function() {
|
||||
return false;});
|
||||
|
||||
window.location = base_url+"/user/"+user+"/"+server_name+"/tree"; }
|
||||
|
||||
function start() {
|
||||
let server_name = (this.id).replace(/^(start-)/, "");
|
||||
// before request
|
||||
$("#start-"+server_name)
|
||||
.attr("disabled", true)
|
||||
.off("click")
|
||||
.click(function() {
|
||||
return false;});
|
||||
|
||||
// request
|
||||
api.start_named_server(user, server_name, {
|
||||
success: function() {
|
||||
// after request --> establish final local state
|
||||
$("#stop-"+server_name)
|
||||
.data("state", true)
|
||||
.show()
|
||||
.attr("disabled", false)
|
||||
.off("click")
|
||||
.click(stop);
|
||||
|
||||
$("#goto-"+server_name)
|
||||
.data("state", true)
|
||||
.show()
|
||||
.attr("disabled", false)
|
||||
.off("click")
|
||||
.click(goto);
|
||||
|
||||
$("#start-"+server_name)
|
||||
.data("state", true)
|
||||
.hide()
|
||||
}});};
|
||||
|
||||
// Initial state: TRUE (server running) -- local state transitions occur by clicking stop
|
||||
// - stop visible
|
||||
// - goto visible
|
||||
// - start invisible
|
||||
|
||||
$("[data-state='true'][id^='stop-']")
|
||||
.show()
|
||||
.attr("disabled", false)
|
||||
.click(stop);
|
||||
|
||||
$("[data-state='true'][id^='goto-']")
|
||||
.show()
|
||||
.attr("disabled", false)
|
||||
.click(goto);
|
||||
|
||||
$("[data-state='true'][id^='start-']")
|
||||
.hide()
|
||||
.attr("disabled", true)
|
||||
.off("click")
|
||||
.click(function() {
|
||||
return false;});
|
||||
|
||||
// Initial state: FALSE (server not running) -- local state transitions occur by clicking start
|
||||
// - stop invisible
|
||||
// - goto invisible
|
||||
// - start visible
|
||||
|
||||
$("[data-state='false'][id^='stop-']")
|
||||
.hide()
|
||||
.attr("disabled", true)
|
||||
.off("click")
|
||||
.click(function() {
|
||||
return false;});
|
||||
|
||||
$("[data-state='false'][id^='goto-']")
|
||||
.hide()
|
||||
.attr("disabled", true)
|
||||
.off("click")
|
||||
.click(function() {
|
||||
return false;});
|
||||
|
||||
$("[data-state='false'][id^='start-']")
|
||||
.show()
|
||||
.attr("disabled", false)
|
||||
.click(start);
|
||||
|
||||
});
|
||||
|
@@ -52,6 +52,15 @@ define(['jquery', 'utils'], function ($, utils) {
|
||||
);
|
||||
};
|
||||
|
||||
JHAPI.prototype.start_named_server = function (user, server_name, options) {
|
||||
options = options || {};
|
||||
options = update(options, {type: 'POST', dataType: null});
|
||||
this.api_request(
|
||||
utils.url_path_join('users', user, 'servers', server_name),
|
||||
options
|
||||
);
|
||||
};
|
||||
|
||||
JHAPI.prototype.stop_server = function (user, options) {
|
||||
options = options || {};
|
||||
options = update(options, {type: 'DELETE', dataType: null});
|
||||
@@ -61,6 +70,15 @@ define(['jquery', 'utils'], function ($, utils) {
|
||||
);
|
||||
};
|
||||
|
||||
JHAPI.prototype.stop_named_server = function (user, server_name, options) {
|
||||
options = options || {};
|
||||
options = update(options, {type: 'DELETE', dataType: null});
|
||||
this.api_request(
|
||||
utils.url_path_join('users', user, 'servers', server_name),
|
||||
options
|
||||
);
|
||||
};
|
||||
|
||||
JHAPI.prototype.list_users = function (options) {
|
||||
this.api_request('users', options);
|
||||
};
|
||||
|
@@ -1,3 +1,3 @@
|
||||
i.sort-icon {
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
|
@@ -40,37 +40,87 @@
|
||||
<a id="shutdown-hub" role="button" class="col-xs-2 col-xs-offset-1 btn btn-danger">Shutdown Hub</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% for u in users %}
|
||||
<tr class="user-row" data-user="{{u.name}}" data-admin="{{u.admin}}">
|
||||
{% for user in users %}
|
||||
<tr class="user-row{% if allow_named_servers %} default-server-row{% endif %}" id="user-{{user.name}}" data-user="{{ user.name }}" data-admin="{{user.admin}}">
|
||||
{% block user_row scoped %}
|
||||
<td class="name-col col-sm-2">{{u.name}}</td>
|
||||
<td class="admin-col col-sm-2">{% if u.admin %}admin{% endif %}</td>
|
||||
<td class="name-col col-sm-2">{{user.name}}</td>
|
||||
<td class="admin-col col-sm-2">{% if user.admin %}admin{% endif %}</td>
|
||||
<td class="time-col col-sm-3">
|
||||
{%- if u.last_activity -%}
|
||||
{{ u.last_activity.isoformat() + 'Z' }}
|
||||
{%- if user.last_activity -%}
|
||||
{{ user.last_activity.isoformat() + 'Z' }}
|
||||
{%- else -%}
|
||||
Never
|
||||
{%- endif -%}
|
||||
</td>
|
||||
<td class="server-col col-sm-2 text-center">
|
||||
<a role="button" class="stop-server btn btn-xs btn-danger {% if not u.running %}hidden{% endif %}">stop server</a>
|
||||
<a role="button" class="start-server btn btn-xs btn-primary {% if u.running %}hidden{% endif %}">start server</a>
|
||||
<a role="button" class="stop-server btn btn-xs btn-danger{% if not user.running %} hidden{% endif %} default-server" data-named_servers={% if allow_named_servers %}"true"{% else %}"false"{% endif %}>
|
||||
{% if allow_named_servers %}
|
||||
stop default server
|
||||
{% else %}
|
||||
stop server
|
||||
{% endif %}
|
||||
</a>
|
||||
<a role="button" class="start-server btn btn-xs btn-primary{% if user.running %} hidden{% endif %} default-server" data-named_servers={% if allow_named_servers %}"true"{% else %}"false"{% endif %}>
|
||||
{% if allow_named_servers %}
|
||||
start default server
|
||||
{% else %}
|
||||
start server</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="server-col col-sm-1 text-center">
|
||||
{% if admin_access %}
|
||||
<a role="button" class="access-server btn btn-xs btn-primary {% if not u.running %}hidden{% endif %}">access server</a>
|
||||
<a role="button" class="access-server btn btn-xs btn-primary{% if not user.running %} hidden{% endif %} default-server" data-named_servers={% if allow_named_servers %}"true"{% else %}"false"{% endif %}>
|
||||
{% if allow_named_servers %}
|
||||
access default server
|
||||
{% else %}
|
||||
access server
|
||||
{% endif %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="edit-col col-sm-1 text-center">
|
||||
<a role="button" class="edit-user btn btn-xs btn-primary">edit</a>
|
||||
<a role="button" class="edit-user btn btn-xs btn-primary">edit{% if allow_named_servers %} {{ user.name }}{% endif %}</a>
|
||||
</td>
|
||||
<td class="edit-col col-sm-1 text-center">
|
||||
{% if u.name != user.name %}
|
||||
<a role="button" class="delete-user btn btn-xs btn-danger">delete</a>
|
||||
{% if user.name != current_user.name %}
|
||||
<a role="button" class="delete-user btn btn-xs btn-danger">delete{% if allow_named_servers %} {{ user.name }}{% endif %}</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% if allow_named_servers %}
|
||||
{% for spawner in user.orm_user._orm_spawners %}
|
||||
{% if spawner.name %}
|
||||
<tr class="user-row" data-user="{{user.name}}" data-admin="{{user.admin}}">
|
||||
{# {% block user_row scoped %} #}
|
||||
<td class="name-col col-sm-2"> </td>
|
||||
<td class="admin-col col-sm-2"> </td>
|
||||
<td class="time-col col-sm-3">
|
||||
{%- if spawner.last_activity -%}
|
||||
{{ spawner.last_activity.isoformat() + 'Z' }}
|
||||
{%- else -%}
|
||||
Never
|
||||
{%- endif -%}
|
||||
</td>
|
||||
<td class="server-col col-sm-2 text-center">
|
||||
<a role="button" class="stop-server btn btn-xs btn-danger{% if not spawner.state %} hidden{% endif %}" id="stop-{{ spawner.name }}">stop {{ spawner.name }}</a>
|
||||
<a role="button" class="start-server btn btn-xs btn-primary{% if spawner.state %} hidden{% endif %}" id="start-{{ spawner.name }}">start {{ spawner.name }}</a>
|
||||
</td>
|
||||
<td class="server-col col-sm-1 text-center">
|
||||
{% if admin_access %}
|
||||
<a role="button" class="access-server btn btn-xs btn-primary{% if not spawner.state %} hidden{% endif %}" id="access-{{ spawner.name }}">access server</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="edit-col col-sm-1 text-center">
|
||||
<!-- edit user button empty for named servers -->
|
||||
</td>
|
||||
<td class="edit-col col-sm-1 text-center">
|
||||
<!-- delete user button empty for named servers -->
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endblock user_row %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
@@ -6,20 +6,76 @@
|
||||
{% block main %}
|
||||
|
||||
<div class="container">
|
||||
{% if user.active %}
|
||||
<div class="row" style="margin: 1%">
|
||||
<div class="text-center">
|
||||
{% if default_server.active %}
|
||||
<a id="stop" role="button" class="btn btn-lg btn-danger">
|
||||
{% if allow_named_servers %}
|
||||
Stop Default Server
|
||||
{% else %}
|
||||
Stop My Server
|
||||
{% endif %}
|
||||
</a>
|
||||
{% if allow_named_servers %}
|
||||
<a id="start" role="button" class="btn btn-lg btn-primary" href="{{ url }}" data-named_servers="true">
|
||||
Default Server
|
||||
{% else %}
|
||||
<a id="start" role="button" class="btn btn-lg btn-primary" href="{{ url }}" data-named_servers="false">
|
||||
My Server
|
||||
{% endif %}
|
||||
</a>
|
||||
{% else %}
|
||||
<a id="start" role="button" class="btn btn-lg btn-primary" href="{{ url }}">
|
||||
Start Default Server
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% for spawner in spawners %}
|
||||
{% if spawner.name %}
|
||||
<div class="row" style="margin: 1%">
|
||||
<div class="text-center">
|
||||
{% if spawner.state %}
|
||||
<a id="stop-{{ spawner.name }}" role="button" class="btn btn-lg btn-danger" data-state="true">
|
||||
Stop {{ spawner.name }}
|
||||
</a>
|
||||
<a id="goto-{{ spawner.name }}" role="button" class="btn btn-lg btn-primary" href="{{ url_path_join(url, spawner.name) }}" data-state="true">
|
||||
{{ spawner.name }}
|
||||
</a>
|
||||
<a id="start-{{ spawner.name }}" role="button" class="btn btn-lg btn-primary" data-state="true" style="display: none">
|
||||
Start {{ spawner.name }}
|
||||
</a>
|
||||
{% else %}
|
||||
<a id="stop-{{ spawner.name }}" role="button" class="btn btn-lg btn-danger" data-state="false" style="display: none">
|
||||
Stop {{ spawner.name }}
|
||||
</a>
|
||||
<a id="goto-{{ spawner.name }}" role="button" class="btn btn-lg btn-primary" href="{{ url_path_join(url, spawner.name) }}" data-state="false" style="display: none">
|
||||
{{ spawner.name }}
|
||||
</a>
|
||||
<a id="start-{{ spawner.name }}" role="button" class="btn btn-lg btn-primary" data-state="false">
|
||||
Start {{ spawner.name }}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% else %}
|
||||
<div class="row">
|
||||
<div class="text-center">
|
||||
{% if user.running %}
|
||||
<a id="stop" role="button" class="btn btn-lg btn-danger">Stop My Server</a>
|
||||
{% endif %}
|
||||
<a id="start" role="button" class="btn btn-lg btn-primary" href="{{ url }}">
|
||||
{% if not user.active %}
|
||||
Start
|
||||
{% if allow_named_servers %}
|
||||
Start a New Server
|
||||
{% else %}
|
||||
Start My Server
|
||||
{% endif %}
|
||||
My Server
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
Reference in New Issue
Block a user