# npm-rails
/node_modules
+/npm-debug.log
//= require morris
//= require jquery.number.min
//= require npm-dependencies
+//= require mithril/stream/stream
//= require_tree .
+window.m = Object.assign(window.Mithril, {stream: window.m.stream})
+
jQuery(function($){
$(document).ajaxStart(function(){
$('.modal-with-loading-spinner .spinner').show();
// Need this to trigger input validation/synchronization callbacks because some browsers
// auto-fill form fields (e.g., when navigating "back" to a page where some text
// had been entered in a search box) without triggering a change or input event.
- $('input').trigger('input');
+ $('input').each(function(el) {
+ $(el).trigger($.Event('input', {currentTarget: el}));
+ });
});
HeaderRowFixer = function(selector) {
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+$(document).on('ready', function() {
+ var db = new window.models.SessionDB()
+ db.checkForNewToken()
+ db.fillMissingUUIDs()
+})
+
+window.components = window.components || {}
+window.components.sessions = {
+ oninit: function(vnode) {
+ vnode.state.db = new window.models.SessionDB()
+ vnode.state.hostToAdd = m.stream('')
+ },
+ view: function(vnode) {
+ var db = vnode.state.db
+ var sessions = db.loadAll()
+ return m('container', [
+ m('table.table.table-condensed.table-hover', m('tbody', [
+ Object.keys(sessions).map(function(uuidPrefix) {
+ var session = sessions[uuidPrefix]
+ return m('tr', [
+ session.token && session.user ? [
+ m('td', m('a.btn.btn-xs.btn-default', {
+ uuidPrefix: uuidPrefix,
+ onclick: m.withAttr('uuidPrefix', db.logout),
+ }, 'log out')),
+ m('td', m('span.label.label-info', 'logged in')),
+ m('td', {title: session.baseURL}, uuidPrefix),
+ m('td', session.user.username),
+ m('td', session.user.email),
+ ] : [
+ m('td', m('a.btn.btn-xs.btn-info', {
+ uuidPrefix: uuidPrefix,
+ onclick: m.withAttr('uuidPrefix', db.login),
+ }, 'log in')),
+ m('td', 'span.label.label-default', 'logged out'),
+ m('td', {title: session.baseURL}, uuidPrefix),
+ m('td'),
+ m('td'),
+ ],
+ m('td', m('a.glyphicon.glyphicon-trash', {
+ uuidPrefix: uuidPrefix,
+ onclick: m.withAttr('uuidPrefix', db.trash),
+ })),
+ ])
+ }),
+ ])),
+ m('.row', m('.col-md-6', [
+ m('form', {
+ onsubmit: function() {
+ db.login(vnode.state.hostToAdd())
+ return false
+ },
+ }, [
+ m('.input-group', [
+ m('input.form-control[type=text][name=apiHost][placeholder="API host"]', {
+ oninput: m.withAttr('value', vnode.state.hostToAdd),
+ }),
+ m('.input-group-btn', [
+ m('input.btn.btn-primary[type=submit][value="Log in"]', {
+ disabled: !vnode.state.hostToAdd(),
+ }),
+ ]),
+ ]),
+ ]),
+ ])),
+ ])
+ },
+}
//
// SPDX-License-Identifier: AGPL-3.0
-// rails_npm does "window.Mithril = require('mithril')" for us.
-var m = window.Mithril
-
$(document).on('ready arv:pane:loaded', function() {
$('[data-mount-mithril]').each(function() {
m.mount(this, window.components[$(this).data('mount-mithril')])
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+window.models = window.models || {}
+window.models.SessionDB = function() {
+ var db = this
+ Object.assign(db, {
+ loadAll: function() {
+ try {
+ return JSON.parse(window.localStorage.getItem('sessions')) || {}
+ } catch(e) {}
+ return {}
+ },
+ save: function(k, v) {
+ var sessions = db.loadAll()
+ sessions[k] = v
+ window.localStorage.setItem('sessions', JSON.stringify(sessions))
+ },
+ trash: function(k) {
+ var sessions = db.loadAll()
+ delete sessions[k]
+ window.localStorage.setItem('sessions', JSON.stringify(sessions))
+ },
+ login: function(host) {
+ // Initiate login procedure with given API host (which can
+ // optionally include scheme://).
+ //
+ // Any page that has a button that invokes login() must
+ // also call checkForNewToken() on (at least) its first
+ // render. Otherwise, the login procedure can't be
+ // completed.
+ var baseURL = host
+ if (baseURL.indexOf('://') < 0)
+ baseURL = 'https://' + baseURL
+ if (!baseURL.endsWith('/'))
+ baseURL = baseURL + '/'
+ document.location = baseURL + 'login?return_to=' + encodeURIComponent(document.location.href+'?baseURL='+encodeURIComponent(baseURL))
+ return false
+ },
+ checkForNewToken: function() {
+ // If there's a token and baseURL in the location bar (i.e.,
+ // we just landed here after a successful login), save it and
+ // scrub the location bar.
+ if (!document.location.search.startsWith('?'))
+ return
+ var params = {}
+ document.location.search.slice(1).split('&').map(function(kv) {
+ var e = kv.indexOf('=')
+ if (e < 0)
+ return
+ params[decodeURIComponent(kv.slice(0, e))] = decodeURIComponent(kv.slice(e+1))
+ })
+ if (!params.baseURL || !params.api_token)
+ // Have a query string, but it's not a login callback.
+ return
+ params.token = params.api_token
+ delete params.api_token
+ db.save(params.baseURL, params)
+ history.replaceState({}, '', document.location.origin + document.location.pathname)
+ },
+ fillMissingUUIDs: function() {
+ var sessions = db.loadAll()
+ Object.keys(sessions).map(function(key) {
+ if (key.indexOf('://') < 0)
+ return
+ // key is the baseURL placeholder. We need to get our user
+ // record to find out the cluster's real uuid prefix.
+ var session = sessions[key]
+ m.request(session.baseURL+'arvados/v1/users/current', {
+ headers: {
+ authorization: 'OAuth2 '+session.token,
+ },
+ }).then(function(user) {
+ session.user = user
+ db.save(user.uuid.slice(0, 5), session)
+ db.trash(key)
+ })
+ })
+ // m.request(session.baseURL + 'discovery/v1/apis/arvados/v1/rest').then(function(dd) {})
+ },
+ })
+}
skip_around_filter :require_thread_api_token, :only => [:destroy, :index]
skip_around_filter :set_thread_api_token, :only => [:destroy, :index]
skip_before_filter :find_object_by_uuid, :only => [:destroy, :index]
+ skip_before_filter :find_objects_for_index
+ skip_before_filter :ensure_arvados_api_exists
def destroy
session.clear
redirect_to arvados_api_client.arvados_logout_url(return_to: root_url)
end
- def index
+ def logged_out
redirect_to root_url if session[:arvados_api_token]
render_index
end
+
+ def index
+ end
end
--- /dev/null
+<!-- Copyright (C) The Arvados Authors. All rights reserved.
+
+SPDX-License-Identifier: AGPL-3.0 -->
+
+<div data-mount-mithril="sessions"></div>
--- /dev/null
+<div data-mount-mithril="test"></div>
# Version of your assets, change this if you want to expire all your assets
config.assets.version = '1.0'
+
+ # npm-rails loads top-level modules like window.Mithril, but we
+ # also pull in some code from node_modules in application.js, like
+ # mithril/stream/stream.
+ config.assets.paths << Rails.root.join('node_modules')
end
end
get '/repositories/:id/tree/:commit/*path' => 'repositories#show_tree', as: :show_repository_tree, format: false
get '/repositories/:id/blob/:commit/*path' => 'repositories#show_blob', as: :show_repository_blob, format: false
get '/repositories/:id/commit/:commit' => 'repositories#show_commit', as: :show_repository_commit
+ resources :sessions
match '/logout' => 'sessions#destroy', via: [:get, :post]
- get '/logged_out' => 'sessions#index'
+ get '/logged_out' => 'sessions#logged_out'
resources :users do
get 'choose', :on => :collection
get 'home', :on => :member
--- /dev/null
+# Copyright (C) The Arvados Authors. All rights reserved.
+#
+# SPDX-License-Identifier: AGPL-3.0
+
+# Run "rake npm:install"
+
+# Browserify is required.
+npm 'browserify', require: false, development: true
+
+npm 'mithril'