Load currentUser/clientConfig from server

This commit is contained in:
Arik Fraimovich 2016-11-23 17:58:00 +02:00
parent 8676eb000d
commit 4ef4f98a66
42 changed files with 200 additions and 159 deletions

View File

@ -0,0 +1,8 @@
<div id="footer">
<a href="http://redash.io">Redash</a> <span ng-bind="$ctrl.version"></span> <small ng-if="$ctrl.newVersionAvailable" ng-cloak class="ng-cloak"><a href="https://version.redash.io/">(New Redash version available)</a></small>
<ul class="f-menu">
<li><a href="https://redash.io/help/">Documentation</a></li>
<li><a href="http://github.com/getredash/redash">Contribute</a></li>
</ul>
</div>

View File

@ -1,10 +1,13 @@
function controller() {
import template from './footer.html';
function controller(clientConfig, currentUser) {
this.version = clientConfig.version;
this.newVersionAvailable = clientConfig.newVersionAvailable && currentUser.isAdmin;
}
export default function (ngModule) {
ngModule.component('footer', {
template: '<div>Footer</div>',
template,
controller,
});
}

View File

@ -16,3 +16,4 @@ export { default as dynamicForm } from './dynamic-form';
export { default as rdTimer } from './rd-timer';
export { default as rdTimeAgo } from './rd-time-ago';
export { default as overlay } from './overlay';
export { default as routeStatus } from './route-status';

View File

@ -0,0 +1,19 @@
export default function (ngModule) {
ngModule.component('routeStatus', {
template: '<overlay ng-if="$ctrl.permissionDenied">You do not have permission to load this page.',
controller($rootScope) {
this.permissionDenied = false;
$rootScope.$on('$routeChangeSuccess', () => {
this.permissionDenied = false;
});
$rootScope.$on('$routeChangeError', (event, current, previous, rejection) => {
if (rejection.status === 403) {
this.permissionDenied = true;
}
});
},
});
}

View File

@ -12,7 +12,10 @@
<body ng-app="app">
<section>
<app-header></app-header>
<route-status></route-status>
<div ng-view></div>
<footer>
</footer>
</section>
</body>
</html>

View File

@ -46,47 +46,6 @@ const requirements = [
const ngModule = angular.module('app', requirements);
// stub for currentUser until we have something real.
const user = {
name: 'Arik Fraimovich',
gravatar_url: 'https://www.gravatar.com/avatar/ca410c2e27337c8d7075bb1b098ac70f?s=40',
id: 1,
groups: [
3,
1,
],
email: 'arik@redash.io',
permissions: [
'admin',
'super_admin',
'create_dashboard',
'create_query',
'edit_dashboard',
'edit_query',
'view_query',
'view_source',
'list_users',
'execute_query',
'schedule_query',
'list_dashboards',
'list_alerts',
'create_alerts',
'list_dashboards',
'list_alerts',
'list_data_sources',
],
isAdmin: true,
};
user.hasPermission = () => true;
user.canEdit = () => true;
ngModule.constant('currentUser', user);
ngModule.constant('clientConfig', { // TODO: make me a service.
showPermissionsControl: true,
allowCustomJSVisualizations: true,
// mailSettingsMissing: true,
});
function registerComponents() {
each(components, (register) => {
register(ngModule);
@ -103,9 +62,14 @@ function registerPages() {
each(pages, (registerPage) => {
const routes = registerPage(ngModule);
function session(Auth) {
return Auth.loadSession();
}
ngModule.config(($routeProvider) => {
each(routes, (route, path) => {
logger('Route: ', path);
route.resolve = Object.assign(route.resolve || {}, { session });
$routeProvider.when(path, route);
});
});
@ -146,4 +110,10 @@ ngModule.config(($routeProvider,
});
});
ngModule.run(($location, Auth) => {
if (!Auth.isAuthenticated()) {
Auth.login();
}
});
export default ngModule;

View File

@ -2,7 +2,7 @@ import moment from 'moment';
import template from './outdated-queries.html';
function OutdatedQueriesCtrl($scope, NgTableParams, currentUser, Events, $http, $timeout) {
Events.record(currentUser, 'view', 'page', 'admin/outdated_queries');
Events.record('view', 'page', 'admin/outdated_queries');
// $scope.$parent.pageTitle = 'Outdated Queries';
$scope.autoUpdate = true;

View File

@ -2,7 +2,7 @@ import template from './status.html';
// TODO: switch to $ctrl instead of $scope.
function AdminStatusCtrl($scope, $http, $timeout, currentUser, Events) {
Events.record(currentUser, 'view', 'page', 'admin/status');
Events.record('view', 'page', 'admin/status');
// $scope.$parent.pageTitle = 'System Status';
const refresh = () => {

View File

@ -20,7 +20,7 @@ function cancelQueryButton() {
queryId = null;
}
Events.record(currentUser, 'cancel_execute', 'query', queryId, { admin: true });
Events.record('cancel_execute', 'query', queryId, { admin: true });
$scope.inProgress = true;
};
},

View File

@ -3,7 +3,7 @@ import template from './tasks.html';
import registerCancelQueryButton from './cancel-query-button';
function TasksCtrl($scope, $location, $http, $timeout, NgTableParams, currentUser, Events) {
Events.record(currentUser, 'view', 'page', 'admin/tasks');
Events.record('view', 'page', 'admin/tasks');
// $scope.$parent.pageTitle = 'Running Queries';
$scope.autoUpdate = true;

View File

@ -8,9 +8,9 @@ function AlertCtrl($routeParams, $location, $sce, toastr, currentUser, Query, Ev
this.alertId = $routeParams.alertId;
if (this.alertId === 'new') {
Events.record(currentUser, 'view', 'page', 'alerts/new');
Events.record('view', 'page', 'alerts/new');
} else {
Events.record(currentUser, 'view', 'alert', this.alertId);
Events.record('view', 'alert', this.alertId);
}
this.trustAsHtml = html => $sce.trustAsHtml(html);

View File

@ -2,7 +2,7 @@ import template from './alerts-list.html';
class AlertsListCtrl {
constructor(NgTableParams, currentUser, Events, Alert) {
Events.record(currentUser, 'view', 'page', 'alerts');
Events.record('view', 'page', 'alerts');
// $scope.$parent.pageTitle = "Alerts";
this.tableParams = new NgTableParams({ count: 50 }, {});

View File

@ -82,7 +82,7 @@ function DashboardCtrl($routeParams, $location, $timeout, $q, $uibModal,
this.loadDashboard = _.throttle((force) => {
this.dashboard = Dashboard.get({ slug: $routeParams.dashboardSlug }, (dashboard) => {
Events.record(currentUser, 'view', 'dashboard', dashboard.id);
Events.record('view', 'dashboard', dashboard.id);
renderDashboard(dashboard, force);
}, () => {
// error...
@ -104,7 +104,7 @@ function DashboardCtrl($routeParams, $location, $timeout, $q, $uibModal,
this.archiveDashboard = () => {
const archive = () => {
Events.record(currentUser, 'archive', 'dashboard', this.dashboard.id);
Events.record('archive', 'dashboard', this.dashboard.id);
this.dashboard.$delete(() => {
// TODO:
// this.$parent.reloadDashboards();

View File

@ -79,7 +79,7 @@ const EditDashboardDialog = {
'Please copy/backup your changes and reload this page.', { autoDismiss: false });
}
});
Events.record(currentUser, 'edit', 'dashboard', this.dashboard.id);
Events.record('edit', 'dashboard', this.dashboard.id);
} else {
$http.post('api/dashboards', {
name: this.dashboard.name,
@ -87,7 +87,7 @@ const EditDashboardDialog = {
this.close();
$location.path(`/dashboard/${response.slug}`).replace();
});
Events.record(currentUser, 'create', 'dashboard');
Events.record('create', 'dashboard');
}
};
},

View File

@ -41,7 +41,7 @@ function DashboardWidgetCtrl($location, $uibModal, $window, Events, currentUser)
return;
}
Events.record(currentUser, 'delete', 'widget', this.widget.id);
Events.record('delete', 'widget', this.widget.id);
this.widget.$delete((response) => {
this.dashboard.widgets =
@ -54,7 +54,7 @@ function DashboardWidgetCtrl($location, $uibModal, $window, Events, currentUser)
});
};
Events.record(currentUser, 'view', 'widget', this.widget.id);
Events.record('view', 'widget', this.widget.id);
this.reload = (force) => {
let maxAge = $location.search().maxAge;
@ -65,8 +65,8 @@ function DashboardWidgetCtrl($location, $uibModal, $window, Events, currentUser)
};
if (this.widget.visualization) {
Events.record(currentUser, 'view', 'query', this.widget.visualization.query.id);
Events.record(currentUser, 'view', 'visualization', this.widget.visualization.id);
Events.record('view', 'query', this.widget.visualization.query.id);
Events.record('view', 'visualization', this.widget.visualization.id);
this.query = this.widget.getQuery();
this.reload(false);

View File

@ -1,7 +1,7 @@
import template from './list.html';
function DataSourcesCtrl($scope, $location, currentUser, Events, DataSource) {
Events.record(currentUser, 'view', 'page', 'admin/data_sources');
Events.record('view', 'page', 'admin/data_sources');
$scope.$parent.pageTitle = 'Data Sources';
$scope.dataSources = DataSource.query();

View File

@ -5,7 +5,7 @@ const logger = debug('redash:http');
function DataSourceCtrl($scope, $routeParams, $http, $location, toastr,
currentUser, Events, DataSource) {
Events.record(currentUser, 'view', 'page', 'admin/data_source');
Events.record('view', 'page', 'admin/data_source');
// $scope.$parent.pageTitle = 'Data Sources';
$scope.dataSourceId = $routeParams.dataSourceId;
@ -23,7 +23,7 @@ function DataSourceCtrl($scope, $routeParams, $http, $location, toastr,
});
function deleteDataSource() {
Events.record(currentUser, 'delete', 'datasource', $scope.dataSource.id);
Events.record('delete', 'datasource', $scope.dataSource.id);
$scope.dataSource.$delete(() => {
toastr.success('Data source deleted successfully.');
@ -35,7 +35,7 @@ function DataSourceCtrl($scope, $routeParams, $http, $location, toastr,
}
function testConnection(callback) {
Events.record(currentUser, 'test', 'datasource', $scope.dataSource.id);
Events.record('test', 'datasource', $scope.dataSource.id);
DataSource.test({ id: $scope.dataSource.id }, (httpResponse) => {
if (httpResponse.ok) {

View File

@ -1,7 +1,7 @@
import template from './list.html';
function DestinationsCtrl($scope, $location, toastr, currentUser, Events, Destination) {
Events.record(currentUser, 'view', 'page', 'admin/destinations');
Events.record('view', 'page', 'admin/destinations');
// $scope.$parent.pageTitle = 'Destinations';
$scope.destinations = Destination.query();

View File

@ -6,7 +6,7 @@ const logger = debug('redash:http');
function DestinationCtrl($scope, $routeParams, $http, $location, toastr,
currentUser, Events, Destination) {
Events.record(currentUser, 'view', 'page', 'admin/destination');
Events.record('view', 'page', 'admin/destination');
$scope.$parent.pageTitle = 'Destinations';
$scope.destinationId = $routeParams.destinationId;
@ -24,7 +24,7 @@ function DestinationCtrl($scope, $routeParams, $http, $location, toastr,
});
$scope.delete = () => {
Events.record(currentUser, 'delete', 'destination', $scope.destination.id);
Events.record('delete', 'destination', $scope.destination.id);
$scope.destination.$delete(() => {
toastr.success('Destination deleted successfully.');

View File

@ -3,7 +3,7 @@ import template from './data-sources.html';
function GroupDataSourcesCtrl($scope, $routeParams, $http, $location, toastr,
currentUser, Events, Group, DataSource) {
Events.record(currentUser, 'view', 'group_data_sources', $scope.groupId);
Events.record('view', 'group_data_sources', $scope.groupId);
$scope.group = Group.get({ id: $routeParams.groupId });
$scope.dataSources = Group.dataSources({ id: $routeParams.groupId });
$scope.newDataSource = {};

View File

@ -2,7 +2,7 @@ import { Paginator } from '../../utils';
import template from './list.html';
function GroupsCtrl($scope, $location, $uibModal, toastr, currentUser, Events, Group) {
Events.record(currentUser, 'view', 'page', 'groups');
Events.record('view', 'page', 'groups');
// $scope.$parent.pageTitle = 'Groups';
$scope.currentUser = currentUser;

View File

@ -3,7 +3,7 @@ import template from './show.html';
function GroupCtrl($scope, $routeParams, $http, $location, toastr,
currentUser, Events, Group, User) {
Events.record(currentUser, 'view', 'group', $scope.groupId);
Events.record('view', 'group', $scope.groupId);
$scope.currentUser = currentUser;
$scope.group = Group.get({ id: $routeParams.groupId });

View File

@ -1,7 +1,7 @@
import template from './home.html';
function HomeCtrl($scope, $uibModal, currentUser, Events, Dashboard, Query) {
Events.record(currentUser, 'view', 'page', 'personal_homepage');
Events.record('view', 'page', 'personal_homepage');
// $scope.$parent.pageTitle = 'Home';
// todo: maybe this should come from some serivce as we have this logic elsewhere.

View File

@ -27,7 +27,7 @@ function QuerySearchCtrl($location, $filter, currentUser, Events, Query) {
}
};
Events.record(currentUser, 'search', 'query', '', { term: this.term });
Events.record('search', 'query', '', { term: this.term });
}
export default function (ngModule) {

View File

@ -9,7 +9,7 @@ function QuerySourceCtrl(Events, toastr, $controller, $scope, $location, $http,
// Obviously it shouldn't be repeated, but we got bigger fish to fry.
const DEFAULT_TAB = 'table';
Events.record(currentUser, 'view_source', 'query', $scope.query.id);
Events.record('view_source', 'query', $scope.query.id);
const isNewQuery = !$scope.query.id;
let queryText = $scope.query.query;
@ -74,7 +74,7 @@ function QuerySourceCtrl(Events, toastr, $controller, $scope, $location, $http,
};
$scope.duplicateQuery = () => {
Events.record(currentUser, 'fork', 'query', $scope.query.id);
Events.record('fork', 'query', $scope.query.id);
$scope.query.name = `Copy of (#${$scope.query.id}) ${$scope.query.name}`;
$scope.query.id = null;
$scope.query.schedule = null;
@ -95,7 +95,7 @@ function QuerySourceCtrl(Events, toastr, $controller, $scope, $location, $http,
const confirm = { class: 'btn-danger', title: 'Delete' };
AlertDialog.open(title, message, confirm).then(() => {
Events.record(currentUser, 'delete', 'visualization', vis.id);
Events.record('delete', 'visualization', vis.id);
Visualization.delete(vis, () => {
if ($scope.selectedTab === vis.id) {

View File

@ -90,7 +90,7 @@ function QueryViewCtrl($scope, Events, $route, $routeParams, $http, $location, $
$scope.showPermissionsControl = clientConfig.showPermissionsControl;
Events.record(currentUser, 'view', 'query', $scope.query.id);
Events.record('view', 'query', $scope.query.id);
if ($scope.query.hasResult() || $scope.query.paramsRequired()) {
getQueryResult();
}
@ -156,12 +156,12 @@ function QueryViewCtrl($scope, Events, $route, $routeParams, $http, $location, $
};
$scope.saveDescription = () => {
Events.record(currentUser, 'edit_description', 'query', $scope.query.id);
Events.record('edit_description', 'query', $scope.query.id);
$scope.saveQuery(undefined, { description: $scope.query.description });
};
$scope.saveName = () => {
Events.record(currentUser, 'edit_name', 'query', $scope.query.id);
Events.record('edit_name', 'query', $scope.query.id);
$scope.saveQuery(undefined, { name: $scope.query.name });
};
@ -177,7 +177,7 @@ function QueryViewCtrl($scope, Events, $route, $routeParams, $http, $location, $
getQueryResult(0);
$scope.lockButton(true);
$scope.cancelling = false;
Events.record(currentUser, 'execute', 'query', $scope.query.id);
Events.record('execute', 'query', $scope.query.id);
Notifications.getPermissions();
};
@ -185,7 +185,7 @@ function QueryViewCtrl($scope, Events, $route, $routeParams, $http, $location, $
$scope.cancelExecution = () => {
$scope.cancelling = true;
$scope.queryResult.cancelExecution();
Events.record(currentUser, 'cancel_execute', 'query', $scope.query.id);
Events.record('cancel_execute', 'query', $scope.query.id);
};
$scope.archiveQuery = () => {
@ -206,7 +206,7 @@ function QueryViewCtrl($scope, Events, $route, $routeParams, $http, $location, $
};
$scope.updateDataSource = () => {
Events.record(currentUser, 'update_data_source', 'query', $scope.query.id);
Events.record('update_data_source', 'query', $scope.query.id);
localStorage.lastSelectedDataSourceId = $scope.query.data_source_id;
$scope.query.latest_query_data = null;

View File

@ -4,7 +4,7 @@ import template from './edit.html';
function SnippetCtrl($routeParams, $http, $location, toastr, currentUser, Events, QuerySnippet) {
// $scope.$parent.pageTitle = 'Query Snippets';
this.snippetId = $routeParams.snippetId;
Events.record(currentUser, 'view', 'query_snippet', this.snippetId);
Events.record('view', 'query_snippet', this.snippetId);
this.editorOptions = {
mode: 'snippets',

View File

@ -2,7 +2,7 @@ import { Paginator } from '../../utils';
import template from './list.html';
function SnippetsCtrl($location, currentUser, Events, QuerySnippet) {
Events.record(currentUser, 'view', 'page', 'query_snippets');
Events.record('view', 'page', 'query_snippets');
// $scope.$parent.pageTitle = 'Query Snippets';
this.snippets = new Paginator([], { itemsPerPage: 20 });

View File

@ -2,7 +2,7 @@ import { Paginator } from '../../utils';
import template from './list.html';
function UsersCtrl($location, toastr, currentUser, Events, User) {
Events.record(currentUser, 'view', 'page', 'users');
Events.record('view', 'page', 'users');
// $scope.$parent.pageTitle = 'Users';
this.currentUser = currentUser;

View File

@ -1,7 +1,7 @@
import template from './new.html';
function NewUserCtrl($scope, $location, toastr, currentUser, Events, User) {
Events.record(currentUser, 'view', 'page', 'users/new');
Events.record('view', 'page', 'users/new');
$scope.user = new User({});
$scope.saveUser = () => {

View File

@ -13,7 +13,7 @@ function UserCtrl($scope, $routeParams, $http, $location, toastr,
$scope.userId = currentUser.id;
}
Events.record(currentUser, 'view', 'user', $scope.userId);
Events.record('view', 'user', $scope.userId);
$scope.canEdit = currentUser.hasPermission('admin') || currentUser.id === parseInt($scope.userId, 10);
$scope.showSettings = false;
$scope.showPasswordSettings = false;

View File

@ -0,0 +1,56 @@
function getLocalSessionData() {
const sessionData = window.sessionStorage.getItem('session');
if (sessionData) {
return JSON.parse(sessionData);
}
return null;
}
function AuthService($window, $location, $q, $http) {
const Auth = {
isAuthenticated() {
return getLocalSessionData() !== null;
},
login() {
// const next = encodeURI($location.url());
console.log('do the login manually!');
// $window.location.href = `http://localhost:5000/default/login?next=${next}`;
},
loadSession() {
const sessionData = getLocalSessionData();
if (sessionData) {
return $q.resolve(sessionData);
}
return $http.get('/api/session').then((response) => {
window.sessionStorage.setItem('session', JSON.stringify(response.data));
return response.data;
});
},
};
return Auth;
}
function CurrentUserService() {
Object.assign(this, getLocalSessionData().user);
this.canEdit = (object) => {
const userId = object.user_id || (object.user && object.user.id);
return this.hasPermission('admin') || (userId && (userId === this.id));
};
this.hasPermission = permission => this.permissions.indexOf(permission) !== -1;
this.isAdmin = this.hasPermission('admin');
}
function ClientConfigService() {
Object.assign(this, getLocalSessionData().client_config);
}
export default function (ngModule) {
ngModule.factory('Auth', AuthService);
ngModule.service('currentUser', CurrentUserService);
ngModule.service('clientConfig', ClientConfigService);
}

View File

@ -10,9 +10,8 @@ function Events($http) {
$http.post('api/events', events);
}, 1000);
this.record = function record(user, action, objectType, objectId, additionalProperties) {
this.record = function record(action, objectType, objectId, additionalProperties) {
const event = {
user_id: user.id,
action,
object_type: objectType,
object_id: objectId,

View File

@ -13,3 +13,4 @@ export { default as QuerySnippet } from './query-snippet';
export { default as Notifications } from './notifications';
export { default as KeyboardShortcuts } from './keyboard-shortcuts';
export { default as AlertDialog } from './alert-dialog';
export { default as Auth } from './auth';

View File

@ -70,7 +70,7 @@ function Notifications(currentUser, Events) {
notification.onclick = function onClick() {
window.focus();
this.close();
Events.record(currentUser, 'click', 'notification');
Events.record('click', 'notification');
};
};

View File

@ -47,9 +47,9 @@ const EditVisualizationDialog = {
this.submit = () => {
if (this.visualization.id) {
Events.record(currentUser, 'update', 'visualization', this.visualization.id, { type: this.visualization.type });
Events.record('update', 'visualization', this.visualization.id, { type: this.visualization.type });
} else {
Events.record(currentUser, 'create', 'visualization', null, { type: this.visualization.type });
Events.record('create', 'visualization', null, { type: this.visualization.type });
}
this.visualization.query_id = this.query.id;

View File

@ -11,7 +11,6 @@
<!-- build:css /styles/main.css -->
<link rel="stylesheet" href="/bower_components/pace/themes/pace-theme-minimal.css">
<link rel="stylesheet" href="/styles/redash.css">
<!-- endbuild -->
<link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32.png">
@ -72,20 +71,7 @@
<script>
// TODO: move currentUser & features to be an Angular service
var clientConfig = {{ client_config|safe }};
var basePath = "{{base_href}}";
var currentUser = {{ user|safe }};
currentUser.canEdit = function(object) {
var user_id = object.user_id || (object.user && object.user.id);
return this.hasPermission('admin') || (user_id && (user_id == currentUser.id));
};
currentUser.hasPermission = function(permission) {
return this.permissions.indexOf(permission) != -1;
};
currentUser.isAdmin = currentUser.hasPermission('admin');
</script>
{% include '_includes/tail.html' %}

View File

@ -1,32 +0,0 @@
(function () {
var dateFormatter = function (value) {
if (!value) {
return "-";
}
return value.format(clientConfig.dateTimeFormat);
};
var MainCtrl = function ($scope, $location, Dashboard) {
$scope.$on("$routeChangeSuccess", function (event, current, previous, rejection) {
if ($scope.showPermissionError) {
$scope.showPermissionError = false;
}
});
$scope.$on("$routeChangeError", function (event, current, previous, rejection) {
if (rejection.status === 403) {
$scope.showPermissionError = true;
}
});
$scope.location = String(document.location);
$scope.version = clientConfig.version;
$scope.newVersionAvailable = clientConfig.newVersionAvailable && currentUser.hasPermission("admin");
};
angular.module('redash.controllers', [])
.controller('IndexCtrl', ['$scope', 'Events', 'Dashboard', 'Query', IndexCtrl])
.controller('MainCtrl', ['$scope', '$location', 'Dashboard', MainCtrl])
.controller('QuerySearchCtrl', ['$scope', '$location', '$filter', 'Events', 'Query', QuerySearchCtrl])
})();

View File

@ -1,18 +1,13 @@
import json
from flask import current_app
from flask_login import login_required
from flask_login import login_required
from redash import models, redis_connection
from redash.utils import json_dumps
from redash.handlers import routes
from redash.handlers.base import json_response
from redash.permissions import require_super_admin
from redash.tasks.queries import QueryTaskTracker
def json_response(response):
return current_app.response_class(json_dumps(response), mimetype='application/json')
@routes.route('/api/admin/queries/outdated', methods=['GET'])
@require_super_admin
@login_required
@ -45,4 +40,3 @@ def queries_tasks():
}
return json_response(response)

View File

@ -1,12 +1,16 @@
import hashlib
import logging
from flask import render_template, request, redirect, url_for, flash
from flask_login import current_user, login_user, logout_user
from redash import models, settings, limiter
from redash.handlers import routes
from redash.handlers.base import org_scoped_rule
from flask import flash, redirect, render_template, request, url_for
from flask_login import current_user, login_user, logout_user
from redash import __version__, models, settings, limiter
from redash.authentication import current_org, get_login_url
from redash.authentication.account import validate_token, BadSignature, SignatureExpired, send_password_reset_email
from redash.authentication.account import (BadSignature, SignatureExpired,
send_password_reset_email,
validate_token)
from redash.handlers import routes
from redash.handlers.base import json_response, org_scoped_rule
from redash.version_check import get_latest_version
logger = logging.getLogger(__name__)
@ -125,3 +129,31 @@ def login(org_slug=None):
def logout(org_slug=None):
logout_user()
return redirect(get_login_url(next=None))
@routes.route(org_scoped_rule('/api/session'), methods=['GET'])
def session(org_slug=None):
email_md5 = hashlib.md5(current_user.email.lower()).hexdigest()
gravatar_url = "https://www.gravatar.com/avatar/%s?s=40" % email_md5
user = {
'gravatar_url': gravatar_url,
'id': current_user.id,
'name': current_user.name,
'email': current_user.email,
'groups': current_user.groups,
'permissions': current_user.permissions
}
client_config = {
'newVersionAvailable': get_latest_version(),
'version': __version__
}
client_config.update(settings.COMMON_CLIENT_CONFIG)
return json_response({
'user': user,
'org_slug': current_org.slug,
'client_config': client_config
})

View File

@ -1,13 +1,14 @@
import time
from flask import request, Blueprint
from flask_restful import Resource, abort
from flask_login import current_user, login_required
from peewee import DoesNotExist
from flask import Blueprint, current_app, request
from flask_login import current_user, login_required
from flask_restful import Resource, abort
from peewee import DoesNotExist
from redash import settings
from redash.tasks import record_event as record_event_task
from redash.models import ApiUser
from redash.authentication import current_org
from redash.models import ApiUser
from redash.tasks import record_event as record_event_task
from redash.utils import json_dumps
routes = Blueprint('redash', __name__, template_folder=settings.fix_assets_path('templates'))
@ -99,3 +100,7 @@ def org_scoped_rule(rule):
return "/<org_slug:org_slug>{}".format(rule)
return rule
def json_response(response):
return current_app.response_class(json_dumps(response), mimetype='application/json')

View File

@ -117,10 +117,6 @@ class QueryResource(BaseResource):
except models.ConflictDetectedError:
abort(409)
# old_query = copy.deepcopy(query.to_dict())
# new_change = query.update_instance_tracked(changing_user=self.current_user, old_object=old_query, **query_def)
# abort(409) # HTTP 'Conflict' status code
result = query.to_dict(with_visualizations=True)
return result