mirror of
https://github.com/portainer/portainer.git
synced 2025-08-04 21:35:23 +02:00
feat(global): add authentication support with single admin account
This commit is contained in:
parent
1e5207517d
commit
4e77c72fa2
35 changed files with 1475 additions and 220 deletions
101
app/components/auth/auth.html
Normal file
101
app/components/auth/auth.html
Normal file
|
@ -0,0 +1,101 @@
|
|||
<div class="login-wrapper">
|
||||
<!-- login box -->
|
||||
<div class="container login-box">
|
||||
<div class="col-md-6 col-md-offset-3 col-sm-6 col-sm-offset-3">
|
||||
<!-- login box logo -->
|
||||
<div class="row">
|
||||
<img ng-if="logo" ng-src="{{ logo }}" class="login-logo">
|
||||
<img ng-if="!logo" src="images/logo_alt.png" class="login-logo" alt="Portainer">
|
||||
</div>
|
||||
<!-- !login box logo -->
|
||||
<!-- init password panel -->
|
||||
<div class="panel panel-default" ng-if="initPassword">
|
||||
<div class="panel-body">
|
||||
<!-- init password form -->
|
||||
<form class="login-form form-horizontal" enctype="multipart/form-data" method="POST">
|
||||
<!-- comment -->
|
||||
<div class="input-group">
|
||||
<p style="margin: 5px;">
|
||||
Please specify a password for the <b>admin</b> user account.
|
||||
</p>
|
||||
</div>
|
||||
<!-- !comment input -->
|
||||
<!-- comment -->
|
||||
<div class="input-group">
|
||||
<p style="margin: 5px;">
|
||||
<i ng-class="{true: 'fa fa-check green-icon', false: 'fa fa-times red-icon'}[initPasswordData.password.length >= 8]" aria-hidden="true"></i>
|
||||
Your password must be at least 8 characters long
|
||||
</p>
|
||||
</div>
|
||||
<!-- !comment input -->
|
||||
<!-- password input -->
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-lock" aria-hidden="true"></i></span>
|
||||
<input id="admin_password" type="password" class="form-control" name="password" ng-model="initPasswordData.password" autofocus>
|
||||
</div>
|
||||
<!-- !password input -->
|
||||
<!-- comment -->
|
||||
<div class="input-group">
|
||||
<p style="margin: 5px;">
|
||||
<i ng-class="{true: 'fa fa-check green-icon', false: 'fa fa-times red-icon'}[initPasswordData.password !== '' && initPasswordData.password === initPasswordData.password_confirmation]" aria-hidden="true"></i>
|
||||
Confirm your password
|
||||
</p>
|
||||
</div>
|
||||
<!-- !comment input -->
|
||||
<!-- password confirmation input -->
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-lock" aria-hidden="true"></i></span>
|
||||
<input id="password_confirmation" type="password" class="form-control" name="password" ng-model="initPasswordData.password_confirmation">
|
||||
</div>
|
||||
<!-- !password confirmation input -->
|
||||
<!-- validate button -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12 controls">
|
||||
<p class="pull-left text-danger" ng-if="initPasswordData.error" style="margin: 5px;">
|
||||
<i class="fa fa-exclamation-circle" aria-hidden="true"></i> Unable to create default user
|
||||
</p>
|
||||
<button type="submit" class="btn btn-primary pull-right" ng-disabled="initPasswordData.password.length < 8 || initPasswordData.password !== initPasswordData.password_confirmation" ng-click="createAdminUser()"><i class="fa fa-key" aria-hidden="true"></i> Validate</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !validate button -->
|
||||
</form>
|
||||
<!-- !init password form -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- !init password panel -->
|
||||
<!-- login panel -->
|
||||
<div class="panel panel-default" ng-if="!initPassword">
|
||||
<div class="panel-body">
|
||||
<!-- login form -->
|
||||
<form class="login-form form-horizontal" enctype="multipart/form-data" method="POST">
|
||||
<!-- username input -->
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-user" aria-hidden="true"></i></span>
|
||||
<input id="username" type="text" class="form-control" name="username" ng-model="authData.username" placeholder="Username">
|
||||
</div>
|
||||
<!-- !username input -->
|
||||
<!-- password input -->
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-lock" aria-hidden="true"></i></span>
|
||||
<input id="password" type="password" class="form-control" name="password" ng-model="authData.password" autofocus>
|
||||
</div>
|
||||
<!-- !password input -->
|
||||
<!-- login button -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12 controls">
|
||||
<p class="pull-left text-danger" ng-if="authData.error" style="margin: 5px;">
|
||||
<i class="fa fa-exclamation-circle" aria-hidden="true"></i> {{ authData.error }}
|
||||
</p>
|
||||
<button type="submit" class="btn btn-primary pull-right" ng-click="authenticateUser()"><i class="fa fa-sign-in" aria-hidden="true"></i> Login</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !login button -->
|
||||
</form>
|
||||
<!-- !login form -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- !login panel -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- !login box -->
|
||||
</div>
|
68
app/components/auth/authController.js
Normal file
68
app/components/auth/authController.js
Normal file
|
@ -0,0 +1,68 @@
|
|||
angular.module('auth', [])
|
||||
.controller('AuthenticationController', ['$scope', '$state', '$stateParams', '$window', '$timeout', '$sanitize', 'Config', 'Authentication', 'Users', 'Messages',
|
||||
function ($scope, $state, $stateParams, $window, $timeout, $sanitize, Config, Authentication, Users, Messages) {
|
||||
|
||||
$scope.authData = {
|
||||
username: 'admin',
|
||||
password: '',
|
||||
error: ''
|
||||
};
|
||||
$scope.initPasswordData = {
|
||||
password: '',
|
||||
password_confirmation: '',
|
||||
error: false
|
||||
};
|
||||
|
||||
if ($stateParams.logout) {
|
||||
Authentication.logout();
|
||||
}
|
||||
|
||||
if ($stateParams.error) {
|
||||
$scope.authData.error = $stateParams.error;
|
||||
Authentication.logout();
|
||||
}
|
||||
|
||||
if (Authentication.isAuthenticated()) {
|
||||
$state.go('dashboard');
|
||||
}
|
||||
|
||||
Config.$promise.then(function (c) {
|
||||
$scope.logo = c.logo;
|
||||
});
|
||||
|
||||
Users.checkAdminUser({}, function (d) {},
|
||||
function (e) {
|
||||
if (e.status === 404) {
|
||||
$scope.initPassword = true;
|
||||
} else {
|
||||
Messages.error("Failure", e, 'Unable to verify administrator account existence');
|
||||
}
|
||||
});
|
||||
|
||||
$scope.createAdminUser = function() {
|
||||
var password = $sanitize($scope.initPasswordData.password);
|
||||
Users.initAdminUser({password: password}, function (d) {
|
||||
$scope.initPassword = false;
|
||||
$timeout(function() {
|
||||
var element = $window.document.getElementById('password');
|
||||
if(element) {
|
||||
element.focus();
|
||||
}
|
||||
});
|
||||
}, function (e) {
|
||||
$scope.initPassword.error = true;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.authenticateUser = function() {
|
||||
$scope.authenticationError = false;
|
||||
var username = $sanitize($scope.authData.username);
|
||||
var password = $sanitize($scope.authData.password);
|
||||
Authentication.login(username, password)
|
||||
.then(function() {
|
||||
$state.go('dashboard');
|
||||
}, function() {
|
||||
$scope.authData.error = 'Invalid credentials';
|
||||
});
|
||||
};
|
||||
}]);
|
|
@ -66,7 +66,7 @@
|
|||
<span ng-show="sortType == 'IP' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
|
||||
</a>
|
||||
</th>
|
||||
<th ng-if="swarm && !swarm_mode">
|
||||
<th ng-if="endpointMode.provider === 'DOCKER_SWARM'">
|
||||
<a ui-sref="containers" ng-click="order('Host')">
|
||||
Host IP
|
||||
<span ng-show="sortType == 'Host' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
|
||||
|
@ -86,11 +86,11 @@
|
|||
<tr dir-paginate="container in (state.filteredContainers = ( containers | filter:state.filter | orderBy:sortType:sortReverse | itemsPerPage: pagination_count))">
|
||||
<td><input type="checkbox" ng-model="container.Checked" ng-change="selectItem(container)"/></td>
|
||||
<td><span class="label label-{{ container.Status|containerstatusbadge }}">{{ container.Status|containerstatus }}</span></td>
|
||||
<td ng-if="swarm && !swarm_mode"><a ui-sref="container({id: container.Id})">{{ container|swarmcontainername}}</a></td>
|
||||
<td ng-if="!swarm || swarm_mode"><a ui-sref="container({id: container.Id})">{{ container|containername}}</a></td>
|
||||
<td ng-if="endpointMode.provider === 'DOCKER_SWARM'"><a ui-sref="container({id: container.Id})">{{ container|swarmcontainername}}</a></td>
|
||||
<td ng-if="endpointMode.provider !== 'DOCKER_SWARM'"><a ui-sref="container({id: container.Id})">{{ container|containername}}</a></td>
|
||||
<td><a ui-sref="image({id: container.Image})">{{ container.Image }}</a></td>
|
||||
<td ng-if="state.displayIP">{{ container.IP ? container.IP : '-' }}</td>
|
||||
<td ng-if="swarm && !swarm_mode">{{ container.hostIP }}</td>
|
||||
<td ng-if="endpointMode.provider === 'DOCKER_SWARM'">{{ container.hostIP }}</td>
|
||||
<td>
|
||||
<a ng-if="container.Ports.length > 0" ng-repeat="p in container.Ports" class="image-tag" ng-href="http://{{p.host}}:{{p.public}}" target="_blank">
|
||||
<i class="fa fa-external-link" aria-hidden="true"></i> {{p.public}}:{{ p.private }}
|
||||
|
|
|
@ -7,9 +7,7 @@ function ($scope, Container, ContainerHelper, Info, Settings, Messages, Config)
|
|||
$scope.sortType = 'State';
|
||||
$scope.sortReverse = false;
|
||||
$scope.state.selectedItemCount = 0;
|
||||
$scope.swarm_mode = false;
|
||||
$scope.pagination_count = Settings.pagination_count;
|
||||
|
||||
$scope.order = function (sortType) {
|
||||
$scope.sortReverse = ($scope.sortType === sortType) ? !$scope.sortReverse : false;
|
||||
$scope.sortType = sortType;
|
||||
|
@ -28,7 +26,7 @@ function ($scope, Container, ContainerHelper, Info, Settings, Messages, Config)
|
|||
if (model.IP) {
|
||||
$scope.state.displayIP = true;
|
||||
}
|
||||
if ($scope.swarm && !$scope.swarm_mode) {
|
||||
if ($scope.endpointMode.provider === 'DOCKER_SWARM') {
|
||||
model.hostIP = $scope.swarm_hosts[_.split(container.Names[0], '/')[1]];
|
||||
}
|
||||
return model;
|
||||
|
@ -150,17 +148,11 @@ function ($scope, Container, ContainerHelper, Info, Settings, Messages, Config)
|
|||
return swarm_hosts;
|
||||
}
|
||||
|
||||
$scope.swarm = false;
|
||||
Config.$promise.then(function (c) {
|
||||
$scope.containersToHideLabels = c.hiddenLabels;
|
||||
$scope.swarm = c.swarm;
|
||||
if (c.swarm) {
|
||||
if (c.swarm && $scope.endpointMode.provider === 'DOCKER_SWARM') {
|
||||
Info.get({}, function (d) {
|
||||
if (!_.startsWith(d.ServerVersion, 'swarm')) {
|
||||
$scope.swarm_mode = true;
|
||||
} else {
|
||||
$scope.swarm_hosts = retrieveSwarmHostsInfo(d);
|
||||
}
|
||||
$scope.swarm_hosts = retrieveSwarmHostsInfo(d);
|
||||
update({all: Settings.displayAll ? 1 : 0});
|
||||
});
|
||||
} else {
|
||||
|
|
|
@ -53,11 +53,6 @@ function ($scope, $state, $stateParams, $filter, Config, Info, Container, Contai
|
|||
|
||||
Config.$promise.then(function (c) {
|
||||
$scope.swarm = c.swarm;
|
||||
Info.get({}, function(info) {
|
||||
if ($scope.swarm && !_.startsWith(info.ServerVersion, 'swarm')) {
|
||||
$scope.swarm_mode = true;
|
||||
}
|
||||
});
|
||||
var containersToHideLabels = c.hiddenLabels;
|
||||
|
||||
Volume.query({}, function (d) {
|
||||
|
@ -216,7 +211,7 @@ function ($scope, $state, $stateParams, $filter, Config, Info, Container, Contai
|
|||
var containerName = container;
|
||||
if (container && typeof container === 'object') {
|
||||
containerName = $filter('trimcontainername')(container.Names[0]);
|
||||
if ($scope.swarm && !$scope.swarm_mode) {
|
||||
if ($scope.swarm && $scope.endpointMode.provider === 'DOCKER_SWARM') {
|
||||
containerName = $filter('swarmcontainername')(container);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -258,7 +258,7 @@
|
|||
<!-- tab-network -->
|
||||
<div class="tab-pane" id="network">
|
||||
<form class="form-horizontal" style="margin-top: 15px;">
|
||||
<div class="form-group" ng-if="globalNetworkCount === 0 && !swarm_mode">
|
||||
<div class="form-group" ng-if="globalNetworkCount === 0 && endpointMode.provider !== 'DOCKER_SWARM_MODE'">
|
||||
<div class="col-sm-12">
|
||||
<span class="small text-muted">You don't have any shared network. Head over the <a ui-sref="networks">networks view</a> to create one.</span>
|
||||
</div>
|
||||
|
@ -278,10 +278,10 @@
|
|||
<div class="form-group" ng-if="config.HostConfig.NetworkMode == 'container'">
|
||||
<label for="container_network_container" class="col-sm-1 control-label text-left">Container</label>
|
||||
<div class="col-sm-9">
|
||||
<select ng-if="(!swarm || swarm && swarm_mode)" ng-options="container|containername for container in runningContainers" class="selectpicker form-control" ng-model="formValues.NetworkContainer">
|
||||
<select ng-if="endpointMode.provider !== 'DOCKER_SWARM'" ng-options="container|containername for container in runningContainers" class="selectpicker form-control" ng-model="formValues.NetworkContainer">
|
||||
<option selected disabled hidden value="">Select a container</option>
|
||||
</select>
|
||||
<select ng-if="swarm && !swarm_mode" ng-options="container|swarmcontainername for container in runningContainers" class="selectpicker form-control" ng-model="formValues.NetworkContainer">
|
||||
<select ng-if="endpointMode.provider === 'DOCKER_SWARM'" ng-options="container|swarmcontainername for container in runningContainers" class="selectpicker form-control" ng-model="formValues.NetworkContainer">
|
||||
<option selected disabled hidden value="">Select a container</option>
|
||||
</select>
|
||||
</div>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
</rd-header>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-xs-12" ng-if="swarm_mode || !swarm">
|
||||
<div class="col-lg-12 col-md-12 col-xs-12" ng-if="endpointMode.provider !== 'DOCKER_SWARM'">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-tachometer" title="Node info"></rd-widget-header>
|
||||
<rd-widget-body classes="no-padding">
|
||||
|
@ -33,7 +33,7 @@
|
|||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
<div class="col-lg-12 col-md-12 col-xs-12" ng-if="swarm && !swarm_mode">
|
||||
<div class="col-lg-12 col-md-12 col-xs-12" ng-if="endpointMode.provider === 'DOCKER_SWARM'">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-tachometer" title="Cluster info"></rd-widget-header>
|
||||
<rd-widget-body classes="no-padding">
|
||||
|
@ -60,7 +60,7 @@
|
|||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
<div class="col-lg-12 col-md-12 col-xs-12" ng-if="swarm && swarm_mode">
|
||||
<div class="col-lg-12 col-md-12 col-xs-12" ng-if="endpointMode.provider === 'DOCKER_SWARM_MODE'">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-tachometer" title="Swarm info"></rd-widget-header>
|
||||
<rd-widget-body classes="no-padding">
|
||||
|
|
|
@ -14,7 +14,6 @@ function ($scope, $q, Config, Container, ContainerHelper, Image, Network, Volume
|
|||
$scope.volumeData = {
|
||||
total: 0
|
||||
};
|
||||
$scope.swarm_mode = false;
|
||||
|
||||
function prepareContainerData(d, containersToHideLabels) {
|
||||
var running = 0;
|
||||
|
@ -64,9 +63,6 @@ function ($scope, $q, Config, Container, ContainerHelper, Image, Network, Volume
|
|||
function prepareInfoData(d) {
|
||||
var info = d;
|
||||
$scope.infoData = info;
|
||||
if ($scope.swarm && !_.startsWith(info.ServerVersion, 'swarm')) {
|
||||
$scope.swarm_mode = true;
|
||||
}
|
||||
}
|
||||
|
||||
function fetchDashboardData(containersToHideLabels) {
|
||||
|
@ -84,6 +80,9 @@ function ($scope, $q, Config, Container, ContainerHelper, Image, Network, Volume
|
|||
prepareNetworkData(d[3]);
|
||||
prepareInfoData(d[4]);
|
||||
$('#loadingViewSpinner').hide();
|
||||
}, function(e) {
|
||||
$('#loadingViewSpinner').hide();
|
||||
Messages.error("Failure", e, "Unable to load dashboard data");
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,31 +1,15 @@
|
|||
angular.module('dashboard')
|
||||
.controller('MasterCtrl', ['$scope', '$cookieStore', 'Settings', 'Config', 'Info',
|
||||
function ($scope, $cookieStore, Settings, Config, Info) {
|
||||
angular.module('main', [])
|
||||
.controller('MainController', ['$scope', '$cookieStore',
|
||||
function ($scope, $cookieStore) {
|
||||
|
||||
/**
|
||||
* Sidebar Toggle & Cookie Control
|
||||
*/
|
||||
var mobileView = 992;
|
||||
|
||||
$scope.getWidth = function() {
|
||||
return window.innerWidth;
|
||||
};
|
||||
|
||||
$scope.swarm_mode = false;
|
||||
|
||||
Config.$promise.then(function (c) {
|
||||
$scope.logo = c.logo;
|
||||
$scope.swarm = c.swarm;
|
||||
Info.get({}, function(d) {
|
||||
if ($scope.swarm && !_.startsWith(d.ServerVersion, 'swarm')) {
|
||||
$scope.swarm_mode = true;
|
||||
$scope.swarm_manager = false;
|
||||
if (d.Swarm.ControlAvailable) {
|
||||
$scope.swarm_manager = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$scope.$watch($scope.getWidth, function(newValue, oldValue) {
|
||||
if (newValue >= mobileView) {
|
||||
if (angular.isDefined($cookieStore.get('toggle'))) {
|
||||
|
@ -47,6 +31,4 @@ function ($scope, $cookieStore, Settings, Config, Info) {
|
|||
window.onresize = function() {
|
||||
$scope.$apply();
|
||||
};
|
||||
|
||||
$scope.uiVersion = Settings.uiVersion;
|
||||
}]);
|
|
@ -23,12 +23,12 @@
|
|||
</div>
|
||||
<!-- !name-input -->
|
||||
<!-- tag-note -->
|
||||
<div class="form-group" ng-if="swarm">
|
||||
<div class="form-group" ng-if="endpointMode.provider === 'DOCKER_SWARM' || endpointMode.provider === 'DOCKER_SWARM_MODE'">
|
||||
<div class="col-sm-12">
|
||||
<span class="small text-muted">Note: The network will be created using the overlay driver and will allow containers to communicate across the hosts of your cluster.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-if="!swarm">
|
||||
<div class="form-group" ng-if="endpointMode.provider === 'DOCKER_STANDALONE'">
|
||||
<div class="col-sm-12">
|
||||
<span class="small text-muted">Note: The network will be created using the bridge driver.</span>
|
||||
</div>
|
||||
|
|
67
app/components/settings/settings.html
Normal file
67
app/components/settings/settings.html
Normal file
|
@ -0,0 +1,67 @@
|
|||
<rd-header>
|
||||
<rd-header-title title="Settings">
|
||||
</rd-header-title>
|
||||
<rd-header-content>Settings</rd-header-content>
|
||||
</rd-header>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-xs-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-lock" title="Change user password"></rd-widget-header>
|
||||
<rd-widget-body>
|
||||
<form class="form-horizontal" style="margin-top: 15px;">
|
||||
<!-- current-password-input -->
|
||||
<div class="form-group">
|
||||
<label for="current_password" class="col-sm-2 control-label text-left">Current password</label>
|
||||
<div class="col-sm-8">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-lock" aria-hidden="true"></i></span>
|
||||
<input type="password" class="form-control" ng-model="formValues.currentPassword" id="current_password">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !current-password-input -->
|
||||
<div class="form-group" style="margin-left: 5px;">
|
||||
<p>
|
||||
<i ng-class="{true: 'fa fa-check green-icon', false: 'fa fa-times red-icon'}[formValues.newPassword.length >= 8]" aria-hidden="true"></i>
|
||||
Your new password must be at least 8 characters long
|
||||
</p>
|
||||
</div>
|
||||
<!-- new-password-input -->
|
||||
<div class="form-group">
|
||||
<label for="new_password" class="col-sm-2 control-label text-left">New password</label>
|
||||
<div class="col-sm-8">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-lock" aria-hidden="true"></i></span>
|
||||
<input type="password" class="form-control" ng-model="formValues.newPassword" id="new_password">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !new-password-input -->
|
||||
<!-- confirm-password-input -->
|
||||
<div class="form-group">
|
||||
<label for="confirm_password" class="col-sm-2 control-label text-left">Confirm password</label>
|
||||
<div class="col-sm-8">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-lock" aria-hidden="true"></i></span>
|
||||
<input type="password" class="form-control" ng-model="formValues.confirmPassword" id="confirm_password">
|
||||
<span class="input-group-addon"><i ng-class="{true: 'fa fa-check green-icon', false: 'fa fa-times red-icon'}[formValues.newPassword !== '' && formValues.newPassword === formValues.confirmPassword]" aria-hidden="true"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !confirm-password-input -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-2">
|
||||
<button type="submit" class="btn btn-primary btn-sm" ng-disabled="!formValues.currentPassword || formValues.newPassword.length < 8 || formValues.newPassword !== formValues.confirmPassword" ng-click="updatePassword()">Update password</button>
|
||||
</div>
|
||||
<div class="col-sm-10">
|
||||
<p class="pull-left text-danger" ng-if="invalidPassword" style="margin: 5px;">
|
||||
<i class="fa fa-exclamation-circle" aria-hidden="true"></i> Current password is not valid
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div>
|
30
app/components/settings/settingsController.js
Normal file
30
app/components/settings/settingsController.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
angular.module('settings', [])
|
||||
.controller('SettingsController', ['$scope', '$state', '$sanitize', 'Users', 'Messages',
|
||||
function ($scope, $state, $sanitize, Users, Messages) {
|
||||
$scope.formValues = {
|
||||
currentPassword: '',
|
||||
newPassword: '',
|
||||
confirmPassword: ''
|
||||
};
|
||||
|
||||
$scope.updatePassword = function() {
|
||||
$scope.invalidPassword = false;
|
||||
$scope.error = false;
|
||||
var currentPassword = $sanitize($scope.formValues.currentPassword);
|
||||
Users.checkPassword({ username: $scope.username, password: currentPassword }, function (d) {
|
||||
if (d.valid) {
|
||||
var newPassword = $sanitize($scope.formValues.newPassword);
|
||||
Users.update({ username: $scope.username, password: newPassword }, function (d) {
|
||||
Messages.send("Success", "Password successfully updated");
|
||||
$state.go('settings', {}, {reload: true});
|
||||
}, function (e) {
|
||||
Messages.error("Failure", e, "Unable to update password");
|
||||
});
|
||||
} else {
|
||||
$scope.invalidPassword = true;
|
||||
}
|
||||
}, function (e) {
|
||||
Messages.error("Failure", e, "Unable to check password validity");
|
||||
});
|
||||
};
|
||||
}]);
|
55
app/components/sidebar/sidebar.html
Normal file
55
app/components/sidebar/sidebar.html
Normal file
|
@ -0,0 +1,55 @@
|
|||
<!-- Sidebar -->
|
||||
<div id="sidebar-wrapper">
|
||||
<ul class="sidebar">
|
||||
<li class="sidebar-main">
|
||||
<a ng-click="toggleSidebar()" class="interactive">
|
||||
<img ng-if="logo" ng-src="{{ logo }}" class="img-responsive logo">
|
||||
<img ng-if="!logo" src="images/logo.png" class="img-responsive logo" alt="Portainer">
|
||||
<span class="menu-icon glyphicon glyphicon-transfer"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="sidebar-title"><span>NAVIGATION</span></li>
|
||||
<li class="sidebar-list">
|
||||
<a ui-sref="dashboard">Dashboard <span class="menu-icon fa fa-tachometer"></span></a>
|
||||
</li>
|
||||
<li class="sidebar-list">
|
||||
<a ui-sref="templates">App Templates <span class="menu-icon fa fa-rocket"></span></a>
|
||||
</li>
|
||||
<li class="sidebar-list" ng-if="endpointMode.provider === 'DOCKER_SWARM_MODE'">
|
||||
<a ui-sref="services">Services <span class="menu-icon fa fa-list-alt"></span></a>
|
||||
</li>
|
||||
<li class="sidebar-list">
|
||||
<a ui-sref="containers">Containers <span class="menu-icon fa fa-server"></span></a>
|
||||
</li>
|
||||
<li class="sidebar-list">
|
||||
<a ui-sref="images">Images <span class="menu-icon fa fa-clone"></span></a>
|
||||
</li>
|
||||
<li class="sidebar-list">
|
||||
<a ui-sref="networks">Networks <span class="menu-icon fa fa-sitemap"></span></a>
|
||||
</li>
|
||||
<li class="sidebar-list">
|
||||
<a ui-sref="volumes">Volumes <span class="menu-icon fa fa-cubes"></span></a>
|
||||
</li>
|
||||
<li class="sidebar-list" ng-if="endpointMode.provider === 'DOCKER_STANDALONE'">
|
||||
<a ui-sref="events">Events <span class="menu-icon fa fa-history"></span></a>
|
||||
</li>
|
||||
<li class="sidebar-list" ng-if="endpointMode.provider === 'DOCKER_SWARM' || (endpointMode.provider === 'DOCKER_SWARM_MODE' && endpointMode.role === 'MANAGER')">
|
||||
<a ui-sref="swarm">Swarm <span class="menu-icon fa fa-object-group"></span></a>
|
||||
</li>
|
||||
<li class="sidebar-list" ng-if="endpointMode.provider === 'DOCKER_STANDALONE'">
|
||||
<a ui-sref="docker">Docker <span class="menu-icon fa fa-th"></span></a>
|
||||
</li>
|
||||
<li class="sidebar-list">
|
||||
<a ui-sref="settings">Settings <span class="menu-icon fa fa-wrench"></span></a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="sidebar-footer">
|
||||
<div class="col-xs-12">
|
||||
<a href="https://github.com/portainer/portainer" target="_blank">
|
||||
<i class="fa fa-github" aria-hidden="true"></i>
|
||||
Portainer {{ uiVersion }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- End Sidebar -->
|
10
app/components/sidebar/sidebarController.js
Normal file
10
app/components/sidebar/sidebarController.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
angular.module('sidebar', [])
|
||||
.controller('SidebarController', ['$scope', 'Settings', 'Config', 'Info',
|
||||
function ($scope, Settings, Config, Info) {
|
||||
|
||||
Config.$promise.then(function (c) {
|
||||
$scope.logo = c.logo;
|
||||
});
|
||||
|
||||
$scope.uiVersion = Settings.uiVersion;
|
||||
}]);
|
|
@ -16,14 +16,14 @@
|
|||
<tbody>
|
||||
<tr>
|
||||
<td>Nodes</td>
|
||||
<td ng-if="!swarm_mode">{{ swarm.Nodes }}</td>
|
||||
<td ng-if="swarm_mode">{{ info.Swarm.Nodes }}</td>
|
||||
<td ng-if="endpointMode.provider === 'DOCKER_SWARM'">{{ swarm.Nodes }}</td>
|
||||
<td ng-if="endpointMode.provider === 'DOCKER_SWARM_MODE'">{{ info.Swarm.Nodes }}</td>
|
||||
</tr>
|
||||
<tr ng-if="!swarm_mode">
|
||||
<tr ng-if="endpointMode.provider === 'DOCKER_SWARM'">
|
||||
<td>Images</td>
|
||||
<td>{{ info.Images }}</td>
|
||||
</tr>
|
||||
<tr ng-if="!swarm_mode">
|
||||
<tr ng-if="endpointMode.provider === 'DOCKER_SWARM'">
|
||||
<td>Swarm version</td>
|
||||
<td>{{ docker.Version|swarmversion }}</td>
|
||||
</tr>
|
||||
|
@ -31,29 +31,29 @@
|
|||
<td>Docker API version</td>
|
||||
<td>{{ docker.ApiVersion }}</td>
|
||||
</tr>
|
||||
<tr ng-if="!swarm_mode">
|
||||
<tr ng-if="endpointMode.provider === 'DOCKER_SWARM'">
|
||||
<td>Strategy</td>
|
||||
<td>{{ swarm.Strategy }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Total CPU</td>
|
||||
<td ng-if="!swarm_mode">{{ info.NCPU }}</td>
|
||||
<td ng-if="swarm_mode">{{ totalCPU }}</td>
|
||||
<td ng-if="endpointMode.provider === 'DOCKER_SWARM'">{{ info.NCPU }}</td>
|
||||
<td ng-if="endpointMode.provider === 'DOCKER_SWARM_MODE'">{{ totalCPU }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Total memory</td>
|
||||
<td ng-if="!swarm_mode">{{ info.MemTotal|humansize: 2 }}</td>
|
||||
<td ng-if="swarm_mode">{{ totalMemory|humansize: 2 }}</td>
|
||||
<td ng-if="endpointMode.provider === 'DOCKER_SWARM'">{{ info.MemTotal|humansize: 2 }}</td>
|
||||
<td ng-if="endpointMode.provider === 'DOCKER_SWARM_MODE'">{{ totalMemory|humansize: 2 }}</td>
|
||||
</tr>
|
||||
<tr ng-if="!swarm_mode">
|
||||
<tr ng-if="endpointMode.provider === 'DOCKER_SWARM'">
|
||||
<td>Operating system</td>
|
||||
<td>{{ info.OperatingSystem }}</td>
|
||||
</tr>
|
||||
<tr ng-if="!swarm_mode">
|
||||
<tr ng-if="endpointMode.provider === 'DOCKER_SWARM'">
|
||||
<td>Kernel version</td>
|
||||
<td>{{ info.KernelVersion }}</td>
|
||||
</tr>
|
||||
<tr ng-if="!swarm_mode">
|
||||
<tr ng-if="endpointMode.provider === 'DOCKER_SWARM'">
|
||||
<td>Go version</td>
|
||||
<td>{{ docker.GoVersion }}</td>
|
||||
</tr>
|
||||
|
@ -65,7 +65,7 @@
|
|||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12" ng-if="!swarm_mode">
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12" ng-if="endpointMode.provider === 'DOCKER_SWARM'">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-hdd-o" title="Node status"></rd-widget-header>
|
||||
<rd-widget-body classes="no-padding">
|
||||
|
@ -133,7 +133,7 @@
|
|||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12" ng-if="swarm_mode">
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12" ng-if="endpointMode.provider === 'DOCKER_SWARM_MODE'">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-hdd-o" title="Node status"></rd-widget-header>
|
||||
<rd-widget-body classes="no-padding">
|
||||
|
|
|
@ -7,7 +7,6 @@ function ($scope, Info, Version, Node, Settings) {
|
|||
$scope.info = {};
|
||||
$scope.docker = {};
|
||||
$scope.swarm = {};
|
||||
$scope.swarm_mode = false;
|
||||
$scope.totalCPU = 0;
|
||||
$scope.totalMemory = 0;
|
||||
$scope.pagination_count = Settings.pagination_count;
|
||||
|
@ -23,8 +22,7 @@ function ($scope, Info, Version, Node, Settings) {
|
|||
|
||||
Info.get({}, function (d) {
|
||||
$scope.info = d;
|
||||
if (!_.startsWith(d.ServerVersion, 'swarm')) {
|
||||
$scope.swarm_mode = true;
|
||||
if ($scope.endpointMode.provider === 'DOCKER_SWARM_MODE') {
|
||||
Node.query({}, function(d) {
|
||||
$scope.nodes = d;
|
||||
var CPU = 0, memory = 0;
|
||||
|
|
|
@ -13,12 +13,12 @@
|
|||
</rd-widget-custom-header>
|
||||
<rd-widget-body classes="padding">
|
||||
<form class="form-horizontal">
|
||||
<div class="form-group" ng-if="globalNetworkCount === 0 && !swarm_mode">
|
||||
<div class="form-group" ng-if="globalNetworkCount === 0 && endpointMode.provider === 'DOCKER_SWARM'">
|
||||
<div class="col-sm-12">
|
||||
<span class="small text-muted">When using Swarm, we recommend deploying containers in a shared network. Looks like you don't have any shared network, head over the <a ui-sref="networks">networks view</a> to create one.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-if="swarm_mode">
|
||||
<div class="form-group" ng-if="endpointMode.provider === 'DOCKER_SWARM_MODE'">
|
||||
<div class="col-sm-12">
|
||||
<i class="fa fa-exclamation-triangle" aria-hidden="true"></i>
|
||||
<span class="small text-muted">App templates cannot be used with swarm-mode at the moment. You can still use them to quickly deploy containers to the Docker host.</span>
|
||||
|
@ -41,10 +41,10 @@
|
|||
<div ng-repeat="var in state.selectedTemplate.env" ng-if="!var.set" class="form-group">
|
||||
<label for="field_{{ $index }}" class="col-sm-2 control-label text-left">{{ var.label }}</label>
|
||||
<div class="col-sm-10">
|
||||
<select ng-if="(!swarm || swarm && swarm_mode) && var.type === 'container'" ng-options="container|containername for container in runningContainers" class="selectpicker form-control" ng-model="var.value">
|
||||
<select ng-if="endpointMode.provider !== 'DOCKER_SWARM' && var.type === 'container'" ng-options="container|containername for container in runningContainers" class="selectpicker form-control" ng-model="var.value">
|
||||
<option selected disabled hidden value="">Select a container</option>
|
||||
</select>
|
||||
<select ng-if="swarm && !swarm_mode && var.type === 'container'" ng-options="container|swarmcontainername for container in runningContainers" class="selectpicker form-control" ng-model="var.value">
|
||||
<select ng-if="endpointMode.provider === 'DOCKER_SWARM' && var.type === 'container'" ng-options="container|swarmcontainername for container in runningContainers" class="selectpicker form-control" ng-model="var.value">
|
||||
<option selected disabled hidden value="">Select a container</option>
|
||||
</select>
|
||||
<input ng-if="!var.type || !var.type === 'container'" type="text" class="form-control" ng-model="var.value" id="field_{{ $index }}">
|
||||
|
|
|
@ -204,11 +204,6 @@ function ($scope, $q, $state, $filter, $anchorScroll, Config, Info, Container, C
|
|||
|
||||
Config.$promise.then(function (c) {
|
||||
$scope.swarm = c.swarm;
|
||||
Info.get({}, function(info) {
|
||||
if ($scope.swarm && !_.startsWith(info.ServerVersion, 'swarm')) {
|
||||
$scope.swarm_mode = true;
|
||||
}
|
||||
});
|
||||
var containersToHideLabels = c.hiddenLabels;
|
||||
Network.query({}, function (d) {
|
||||
var networks = d;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue