#981 Login unified without passing tests
This commit is contained in:
parent
0f55512918
commit
e080802445
|
@ -0,0 +1,45 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethod('acl', {
|
||||||
|
description: 'Get the user information and permissions',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'ctx',
|
||||||
|
type: 'Object',
|
||||||
|
http: {source: 'context'}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
returns: {
|
||||||
|
type: 'Object',
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/acl`,
|
||||||
|
verb: 'GET'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.acl = async function(ctx) {
|
||||||
|
let userId = ctx.req.accessToken.userId;
|
||||||
|
let models = Self.app.models;
|
||||||
|
|
||||||
|
let user = await models.Account.findById(userId, {
|
||||||
|
fields: ['id', 'name', 'nickname', 'email']
|
||||||
|
});
|
||||||
|
|
||||||
|
let roles = await models.RoleMapping.find({
|
||||||
|
fields: ['roleId'],
|
||||||
|
where: {
|
||||||
|
principalId: userId,
|
||||||
|
principalType: 'USER'
|
||||||
|
},
|
||||||
|
include: [{
|
||||||
|
relation: 'role',
|
||||||
|
scope: {
|
||||||
|
fields: ['name']
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
|
||||||
|
return {roles, user};
|
||||||
|
};
|
||||||
|
};
|
|
@ -1,4 +1,3 @@
|
||||||
const url = require('url');
|
|
||||||
const md5 = require('md5');
|
const md5 = require('md5');
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
|
@ -13,12 +12,7 @@ module.exports = Self => {
|
||||||
}, {
|
}, {
|
||||||
arg: 'password',
|
arg: 'password',
|
||||||
type: 'String',
|
type: 'String',
|
||||||
description: 'The user name or email',
|
description: 'The user name or email'
|
||||||
required: true
|
|
||||||
}, {
|
|
||||||
arg: 'location',
|
|
||||||
type: 'String',
|
|
||||||
description: 'Location to redirect after login'
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
returns: {
|
returns: {
|
||||||
|
@ -31,7 +25,7 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.login = async function(user, password, location) {
|
Self.login = async function(user, password) {
|
||||||
let token;
|
let token;
|
||||||
let usesEmail = user.indexOf('@') !== -1;
|
let usesEmail = user.indexOf('@') !== -1;
|
||||||
let User = Self.app.models.User;
|
let User = Self.app.models.User;
|
||||||
|
@ -68,25 +62,6 @@ module.exports = Self => {
|
||||||
token = await User.login(loginInfo, 'user');
|
token = await User.login(loginInfo, 'user');
|
||||||
}
|
}
|
||||||
|
|
||||||
let apiKey;
|
return {token: token.id};
|
||||||
let continueUrl;
|
|
||||||
|
|
||||||
try {
|
|
||||||
let query = url.parse(location, true).query;
|
|
||||||
apiKey = query.apiKey;
|
|
||||||
continueUrl = query.continue;
|
|
||||||
} catch (e) {
|
|
||||||
continueUrl = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
let applications = Self.app.get('applications');
|
|
||||||
if (!apiKey) apiKey = 'default';
|
|
||||||
let loginUrl = applications[apiKey] || '/login';
|
|
||||||
|
|
||||||
return {
|
|
||||||
token: token.id,
|
|
||||||
continue: continueUrl,
|
|
||||||
loginUrl: loginUrl
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
const app = require(`${serviceRoot}/server/server`);
|
||||||
|
|
||||||
|
describe('account login()', () => {
|
||||||
|
it('when the user doesnt exist but the client does and the password is correct', async() => {
|
||||||
|
let response = await app.models.Account.login('PetterParker', 'nightmare');
|
||||||
|
|
||||||
|
expect(response.token).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the user exists and the password is correct', () => {
|
||||||
|
it('should login and return the token', async() => {
|
||||||
|
let response = await app.models.Account.login('employee', 'nightmare');
|
||||||
|
|
||||||
|
expect(response.token).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should define the url to continue upon login', async() => {
|
||||||
|
let location = 'http://localhost/auth/?apiKey=salix&continue=continueURL';
|
||||||
|
let response = await app.models.Account.login('employee', 'nightmare', location);
|
||||||
|
|
||||||
|
expect(response.continue).toBe('continueURL');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should define the loginUrl upon login', async() => {
|
||||||
|
let location = 'http://localhost/auth/?apiKey=salix';
|
||||||
|
let response = await app.models.Account.login('employee', 'nightmare', location);
|
||||||
|
|
||||||
|
expect(response.loginUrl).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw a 401 error when credentials are incorrect', async() => {
|
||||||
|
let error;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await app.models.Account.login('IDontExist', 'TotallyWrongPassword');
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error).toBeDefined();
|
||||||
|
expect(error.statusCode).toBe(401);
|
||||||
|
expect(error.code).toBe('LOGIN_FAILED');
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,42 @@
|
||||||
|
const app = require(`${serviceRoot}/server/server`);
|
||||||
|
|
||||||
|
describe('account logout()', () => {
|
||||||
|
it('should logout and remove token after valid login', async() => {
|
||||||
|
let loginResponse = await app.models.Account.login('employee', 'nightmare');
|
||||||
|
let accessToken = await app.models.AccessToken.findById(loginResponse.token);
|
||||||
|
let ctx = {req: {accessToken: accessToken}};
|
||||||
|
|
||||||
|
let response = await app.models.Account.logout(ctx);
|
||||||
|
let afterToken = await app.models.AccessToken.findById(loginResponse.token);
|
||||||
|
|
||||||
|
expect(response).toBeTruthy();
|
||||||
|
expect(afterToken).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw a 401 error when token is invalid', async() => {
|
||||||
|
let error;
|
||||||
|
let ctx = {req: {accessToken: {id: 'invalidToken'}}};
|
||||||
|
|
||||||
|
try {
|
||||||
|
response = await app.models.Account.logout(ctx);
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error).toBeDefined();
|
||||||
|
expect(error.statusCode).toBe(401);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error when no token is passed', async() => {
|
||||||
|
let error;
|
||||||
|
let ctx = {req: {accessToken: null}};
|
||||||
|
|
||||||
|
try {
|
||||||
|
response = await app.models.Account.logout(ctx);
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,31 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethod('validateToken', {
|
||||||
|
description: 'Get the user information and permissions',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'token',
|
||||||
|
type: 'String',
|
||||||
|
description: 'The token to validate',
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
returns: {
|
||||||
|
type: 'Boolean',
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/validateToken`,
|
||||||
|
verb: 'GET'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.validateToken = function(tokenId, cb) {
|
||||||
|
Self.app.models.AccessToken.findById(tokenId, (err, token) => {
|
||||||
|
if (err) return cb(err);
|
||||||
|
if (token)
|
||||||
|
token.validate((_, isValid) => cb(null, isValid === true));
|
||||||
|
else
|
||||||
|
cb(null, false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
|
@ -3,6 +3,8 @@ const md5 = require('md5');
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
require('../methods/account/login')(Self);
|
require('../methods/account/login')(Self);
|
||||||
require('../methods/account/logout')(Self);
|
require('../methods/account/logout')(Self);
|
||||||
|
require('../methods/account/acl')(Self);
|
||||||
|
require('../methods/account/validate-token')(Self);
|
||||||
|
|
||||||
// Validations
|
// Validations
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,9 @@
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
|
"nickname": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"password": {
|
"password": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"required": true
|
"required": true
|
||||||
|
@ -45,6 +48,12 @@
|
||||||
"principalType": "ROLE",
|
"principalType": "ROLE",
|
||||||
"principalId": "$authenticated",
|
"principalId": "$authenticated",
|
||||||
"permission": "ALLOW"
|
"permission": "ALLOW"
|
||||||
|
}, {
|
||||||
|
"property": "validateToken",
|
||||||
|
"accessType": "EXECUTE",
|
||||||
|
"principalType": "ROLE",
|
||||||
|
"principalId": "$everyone",
|
||||||
|
"permission": "ALLOW"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>Salix</title>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
</head>
|
|
||||||
<body ng-app="vnAuth">
|
|
||||||
<vn-login></vn-login>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,2 +0,0 @@
|
||||||
import './module';
|
|
||||||
import './login/login';
|
|
|
@ -1,29 +0,0 @@
|
||||||
<div>
|
|
||||||
<div class="box-wrapper">
|
|
||||||
<div class="box">
|
|
||||||
<img src="./logo.svg"/>
|
|
||||||
<form name="form" ng-submit="$ctrl.submit()">
|
|
||||||
<vn-textfield
|
|
||||||
label="User"
|
|
||||||
model="$ctrl.user"
|
|
||||||
name="user"
|
|
||||||
vn-id="userField"
|
|
||||||
vn-focus>
|
|
||||||
</vn-textfield>
|
|
||||||
<vn-textfield
|
|
||||||
label="Password"
|
|
||||||
model="$ctrl.password"
|
|
||||||
name="password"
|
|
||||||
type="password">
|
|
||||||
</vn-textfield>
|
|
||||||
<div class="footer">
|
|
||||||
<vn-submit label="Enter"></vn-submit>
|
|
||||||
<div class="spinner-wrapper">
|
|
||||||
<vn-spinner enable="$ctrl.loading"></vn-spinner>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<vn-snackbar vn-id="snackbar"></vn-snackbar>
|
|
||||||
</div>
|
|
|
@ -1,86 +0,0 @@
|
||||||
import ngModule from '../module';
|
|
||||||
import './style.scss';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A simple login form.
|
|
||||||
*/
|
|
||||||
export default class Controller {
|
|
||||||
constructor($element, $scope, $window, $http) {
|
|
||||||
this.$element = $element;
|
|
||||||
this.$ = $scope;
|
|
||||||
this.$window = $window;
|
|
||||||
this.$http = $http;
|
|
||||||
}
|
|
||||||
submit() {
|
|
||||||
if (!this.user) {
|
|
||||||
this.focusUser();
|
|
||||||
this.showError('Please insert your user and password');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loading = true;
|
|
||||||
let params = {
|
|
||||||
user: this.user,
|
|
||||||
password: this.password,
|
|
||||||
location: this.$window.location.href
|
|
||||||
};
|
|
||||||
this.$http.post('/auth/login', params).then(
|
|
||||||
json => this.onLoginOk(json),
|
|
||||||
json => this.onLoginErr(json)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
onLoginOk(json) {
|
|
||||||
this.loading = false;
|
|
||||||
let data = json.data;
|
|
||||||
let params = {
|
|
||||||
token: data.token,
|
|
||||||
continue: data.continue
|
|
||||||
};
|
|
||||||
let loginUrl = data.loginUrl || '';
|
|
||||||
this.$window.location = `${loginUrl}?${this.encodeUri(params)}`;
|
|
||||||
}
|
|
||||||
encodeUri(object) {
|
|
||||||
let uri = '';
|
|
||||||
for (let key in object) {
|
|
||||||
if (object[key] !== undefined) {
|
|
||||||
if (uri.length > 0)
|
|
||||||
uri += '&';
|
|
||||||
uri += encodeURIComponent(key) + '=' + encodeURIComponent(object[key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return uri;
|
|
||||||
}
|
|
||||||
onLoginErr(json) {
|
|
||||||
this.loading = false;
|
|
||||||
this.password = '';
|
|
||||||
|
|
||||||
let message;
|
|
||||||
|
|
||||||
switch (json.status) {
|
|
||||||
case 401:
|
|
||||||
message = 'Invalid credentials';
|
|
||||||
break;
|
|
||||||
case -1:
|
|
||||||
message = 'Can\'t contact with server';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
message = 'Something went wrong';
|
|
||||||
}
|
|
||||||
|
|
||||||
this.showError(message);
|
|
||||||
this.focusUser();
|
|
||||||
}
|
|
||||||
focusUser() {
|
|
||||||
this.$.userField.select();
|
|
||||||
this.$.userField.focus();
|
|
||||||
}
|
|
||||||
showError(message) {
|
|
||||||
this.$.snackbar.showError({message: message});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Controller.$inject = ['$element', '$scope', '$window', '$http'];
|
|
||||||
|
|
||||||
ngModule.component('vnLogin', {
|
|
||||||
template: require('./login.html'),
|
|
||||||
controller: Controller
|
|
||||||
});
|
|
|
@ -1,51 +0,0 @@
|
||||||
@import "colors";
|
|
||||||
|
|
||||||
vn-login > div {
|
|
||||||
position: absolute;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
color: $main-font-color;
|
|
||||||
font-size: 1.1em;
|
|
||||||
font-weight: normal;
|
|
||||||
background-color: #3c393b;
|
|
||||||
|
|
||||||
.box-wrapper {
|
|
||||||
position: relative;
|
|
||||||
max-width: 19em;
|
|
||||||
margin: auto;
|
|
||||||
height: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.box {
|
|
||||||
box-sizing: border-box;
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
width: 100%;
|
|
||||||
margin-top: -13.5em;
|
|
||||||
padding: 3em;
|
|
||||||
background-color: white;
|
|
||||||
box-shadow: 0 0 1em 0 rgba(1,1,1,.6);
|
|
||||||
border-radius: .5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 100%;
|
|
||||||
padding-bottom: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
|
||||||
margin-top: 1em;
|
|
||||||
text-align: center;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.spinner-wrapper {
|
|
||||||
position: absolute;
|
|
||||||
width: 0;
|
|
||||||
top: .3em;
|
|
||||||
right: 3em;
|
|
||||||
overflow: visible;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
import {ng} from 'core/vendor';
|
|
||||||
import 'core';
|
|
||||||
|
|
||||||
let ngModule = ng.module('vnAuth', ['vnCore']);
|
|
||||||
export default ngModule;
|
|
||||||
|
|
||||||
config.$inject = ['$translatePartialLoaderProvider', '$httpProvider'];
|
|
||||||
export function config($translatePartialLoaderProvider, $httpProvider) {
|
|
||||||
$translatePartialLoaderProvider.addPart('auth');
|
|
||||||
|
|
||||||
$httpProvider.defaults.useXDomain = true;
|
|
||||||
delete $httpProvider.defaults.headers.common['X-Requested-With'];
|
|
||||||
}
|
|
||||||
ngModule.config(config);
|
|
|
@ -16,9 +16,8 @@ function vnAcl(aclService, $timeout) {
|
||||||
let find = input.className.match(/mdl-[\w]+input/g);
|
let find = input.className.match(/mdl-[\w]+input/g);
|
||||||
if (find && find.length && find[0]) {
|
if (find && find.length && find[0]) {
|
||||||
let type = getMaterialType(find[0]);
|
let type = getMaterialType(find[0]);
|
||||||
if (type && input.parentNode[`Material${type}`] && input.parentNode[`Material${type}`].updateClasses_) {
|
if (type && input.parentNode[`Material${type}`] && input.parentNode[`Material${type}`].updateClasses_)
|
||||||
input.parentNode[`Material${type}`].updateClasses_();
|
input.parentNode[`Material${type}`].updateClasses_();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,7 +37,7 @@ function vnAcl(aclService, $timeout) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function permissionElement($element, action) {
|
function permissionElement($element, action) {
|
||||||
if (!aclService.aclPermission(acls)) {
|
if (!aclService.hasAny(acls)) {
|
||||||
if (action === 'disabled') {
|
if (action === 'disabled') {
|
||||||
let input = $element[0];
|
let input = $element[0];
|
||||||
let selector = 'input, textarea, button, submit';
|
let selector = 'input, textarea, button, submit';
|
||||||
|
@ -48,25 +47,24 @@ function vnAcl(aclService, $timeout) {
|
||||||
|
|
||||||
if (input) {
|
if (input) {
|
||||||
$timeout(() => {
|
$timeout(() => {
|
||||||
input.setAttribute("disabled", "true");
|
input.setAttribute('disabled', 'true');
|
||||||
updateMaterial(input);
|
updateMaterial(input);
|
||||||
});
|
});
|
||||||
$element[0].querySelectorAll('vn-drop-down').forEach(element => {
|
$element[0].querySelectorAll('vn-drop-down').forEach(element => {
|
||||||
element.parentNode.removeChild(element);
|
element.parentNode.removeChild(element);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else
|
||||||
$element.remove();
|
$element.remove();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateAcls(role, toAdd) {
|
function updateAcls(role, toAdd) {
|
||||||
let position = acls.indexOf(role);
|
let position = acls.indexOf(role);
|
||||||
|
|
||||||
if (!toAdd && position > -1) {
|
if (!toAdd && position > -1)
|
||||||
acls.splice(position, 1);
|
acls.splice(position, 1);
|
||||||
} // todo: add acl and enabled element if previusly was disabled
|
// todo: add acl and enabled element if previusly was disabled
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -8,7 +8,7 @@ describe('Directive acl', () => {
|
||||||
|
|
||||||
compile = (hasPermissions, _element) => {
|
compile = (hasPermissions, _element) => {
|
||||||
inject(($compile, $rootScope, aclService, _$timeout_) => {
|
inject(($compile, $rootScope, aclService, _$timeout_) => {
|
||||||
spyOn(aclService, 'aclPermission').and.returnValue(hasPermissions);
|
spyOn(aclService, 'hasAny').and.returnValue(hasPermissions);
|
||||||
scope = $rootScope.$new();
|
scope = $rootScope.$new();
|
||||||
$timeout = _$timeout_;
|
$timeout = _$timeout_;
|
||||||
element = angular.element(_element);
|
element = angular.element(_element);
|
||||||
|
|
|
@ -6,9 +6,8 @@ function vnVisibleBy(aclService) {
|
||||||
priority: -1,
|
priority: -1,
|
||||||
link: function($scope, $element, $attrs) {
|
link: function($scope, $element, $attrs) {
|
||||||
let acls = $attrs.vnVisibleBy.split(',');
|
let acls = $attrs.vnVisibleBy.split(',');
|
||||||
if (!aclService.aclPermission(acls)) {
|
if (!aclService.hasAny(acls))
|
||||||
$element[0].style.visibility = 'hidden';
|
$element[0].style.visibility = 'hidden';
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,43 +1,34 @@
|
||||||
import ngModule from '../module';
|
import ngModule from '../module';
|
||||||
|
|
||||||
var acl = window.salix ? window.salix.acl : {};
|
class AclService {
|
||||||
ngModule.constant('aclConstant', acl);
|
constructor($http) {
|
||||||
|
this.$http = $http;
|
||||||
aclService.$inject = ['aclConstant'];
|
|
||||||
function aclService(aclConstant) {
|
|
||||||
if (aclConstant.roles) {
|
|
||||||
this.roles = {};
|
|
||||||
Object.keys(aclConstant.roles).forEach(role => {
|
|
||||||
this.roles[role.toLowerCase()] = aclConstant.roles[role];
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.roles = undefined;
|
|
||||||
}
|
}
|
||||||
|
reset() {
|
||||||
|
this.user = null;
|
||||||
|
this.roles = null;
|
||||||
|
}
|
||||||
|
load() {
|
||||||
|
return this.$http.get('/api/Accounts/acl').then(res => {
|
||||||
|
this.user = res.data.user;
|
||||||
|
this.roles = {};
|
||||||
|
|
||||||
this.routeHasPermission = function(route) {
|
for (let role of res.data.roles) {
|
||||||
let hasPermission;
|
if (role.role)
|
||||||
if (!this.roles)
|
this.roles[role.role.name] = true;
|
||||||
hasPermission = false;
|
}
|
||||||
else if (!route.acl)
|
});
|
||||||
hasPermission = true;
|
}
|
||||||
else if (!this.roles || !Object.keys(this.roles).length)
|
hasAny(roles) {
|
||||||
hasPermission = false;
|
if (this.roles) {
|
||||||
else
|
for (let role of roles) {
|
||||||
hasPermission = this.aclPermission(route.acl);
|
if (this.roles[role])
|
||||||
return hasPermission;
|
return true;
|
||||||
};
|
|
||||||
this.aclPermission = function(aclCollection) {
|
|
||||||
let hasPermission = false;
|
|
||||||
let total = aclCollection.length;
|
|
||||||
for (let i = 0; i < total; i++) {
|
|
||||||
let role = aclCollection[i].trim().toLowerCase();
|
|
||||||
if (this.roles[role]) {
|
|
||||||
hasPermission = true;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return hasPermission;
|
return false;
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
AclService.$inject = ['$http'];
|
||||||
|
|
||||||
ngModule.service('aclService', aclService);
|
ngModule.service('aclService', AclService);
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import './module-loader';
|
import './module-loader';
|
||||||
import './crud';
|
import './crud';
|
||||||
import './app';
|
import './app';
|
||||||
import './interceptor';
|
|
||||||
import './acl-service';
|
import './acl-service';
|
||||||
import './storage-services';
|
import './storage-services';
|
||||||
import './template';
|
import './template';
|
||||||
|
|
|
@ -22,13 +22,13 @@ export function factory($http, $window, $ocLazyLoad, $translatePartialLoader, $t
|
||||||
let deps = splitingRegister.getDependencies(moduleName);
|
let deps = splitingRegister.getDependencies(moduleName);
|
||||||
let depPromises = [];
|
let depPromises = [];
|
||||||
|
|
||||||
if (deps)
|
if (deps) {
|
||||||
for (let dep of deps)
|
for (let dep of deps)
|
||||||
depPromises.push(this.load(dep, validations));
|
depPromises.push(this.load(dep, validations));
|
||||||
|
}
|
||||||
|
|
||||||
loaded[moduleName] = new Promise((resolve, reject) => {
|
loaded[moduleName] = new Promise((resolve, reject) => {
|
||||||
Promise.all(depPromises)
|
Promise.all(depPromises).then(() => {
|
||||||
.then(() => {
|
|
||||||
let promises = [];
|
let promises = [];
|
||||||
|
|
||||||
$translatePartialLoader.addPart(moduleName);
|
$translatePartialLoader.addPart(moduleName);
|
||||||
|
@ -39,26 +39,24 @@ export function factory($http, $window, $ocLazyLoad, $translatePartialLoader, $t
|
||||||
);
|
);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (validations)
|
if (validations) {
|
||||||
promises.push(new Promise(resolve => {
|
promises.push(new Promise(resolve => {
|
||||||
$http.get(`/${moduleName}/validations`).then(
|
$http.get(`/${moduleName}/validations`).then(
|
||||||
json => this.onValidationsReady(json, resolve),
|
json => this.onValidationsReady(json, resolve),
|
||||||
() => resolve()
|
() => resolve()
|
||||||
);
|
);
|
||||||
}));
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
promises.push(new Promise(resolve => {
|
promises.push(new Promise(resolve => {
|
||||||
splitingRegister.modules[moduleName](resolve);
|
splitingRegister.modules[moduleName](resolve);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
Promise.all(promises)
|
Promise.all(promises).then(() => {
|
||||||
.then(() => {
|
|
||||||
loaded[moduleName] = true;
|
loaded[moduleName] = true;
|
||||||
resolve($ocLazyLoad.load({name: moduleName}));
|
resolve($ocLazyLoad.load({name: moduleName}));
|
||||||
})
|
}).catch(reject);
|
||||||
.catch(reject);
|
}).catch(reject);
|
||||||
})
|
|
||||||
.catch(reject);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return loaded[moduleName];
|
return loaded[moduleName];
|
||||||
|
|
|
@ -28,21 +28,21 @@ describe('Service acl', () => {
|
||||||
expect(aclService.routeHasPermission(route)).toBeFalsy();
|
expect(aclService.routeHasPermission(route)).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call the service aclPermission() function and return false as the service has roles and the rote has acl without length', () => {
|
it('should call the service hasAny() function and return false as the service has roles and the rote has acl without length', () => {
|
||||||
aclService.roles = {customer: true, employee: true};
|
aclService.roles = {customer: true, employee: true};
|
||||||
let route = {url: 'http://www.verdnatura.es', acl: []};
|
let route = {url: 'http://www.verdnatura.es', acl: []};
|
||||||
spyOn(aclService, 'aclPermission').and.callThrough();
|
spyOn(aclService, 'hasAny').and.callThrough();
|
||||||
|
|
||||||
expect(aclService.routeHasPermission(route)).toBeFalsy();
|
expect(aclService.routeHasPermission(route)).toBeFalsy();
|
||||||
expect(aclService.aclPermission).toHaveBeenCalledWith(route.acl);
|
expect(aclService.hasAny).toHaveBeenCalledWith(route.acl);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call the service aclPermission() function to return true as the service has roles matching with the ones in acl', () => {
|
it('should call the service hasAny() function to return true as the service has roles matching with the ones in acl', () => {
|
||||||
aclService.roles = {customer: true, employee: true};
|
aclService.roles = {customer: true, employee: true};
|
||||||
let route = {url: 'http://www.verdnatura.es', acl: ['customer']};
|
let route = {url: 'http://www.verdnatura.es', acl: ['customer']};
|
||||||
spyOn(aclService, 'aclPermission').and.callThrough();
|
spyOn(aclService, 'hasAny').and.callThrough();
|
||||||
|
|
||||||
expect(aclService.routeHasPermission(route)).toBeTruthy();
|
expect(aclService.routeHasPermission(route)).toBeTruthy();
|
||||||
expect(aclService.aclPermission).toHaveBeenCalledWith(route.acl);
|
expect(aclService.hasAny).toHaveBeenCalledWith(route.acl);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,11 +1,23 @@
|
||||||
<vn-topbar vn-auto>
|
<vn-topbar ng-if="$ctrl.inApp">
|
||||||
<a ui-sref="home" title="{{'Home' | translate}}">
|
<a class="logo" ui-sref="home" title="{{'Home' | translate}}">
|
||||||
<img class="logo" src="./logo.svg" alt="Logo"></img>
|
<img src="./logo.svg" alt="Logo"></img>
|
||||||
</a>
|
</a>
|
||||||
|
<vn-icon
|
||||||
|
class="show-menu"
|
||||||
|
icon="menu"
|
||||||
|
ng-click="$ctrl.showMenu()">
|
||||||
|
</vn-icon>
|
||||||
<vn-spinner enable="$ctrl.vnApp.loading"></vn-spinner>
|
<vn-spinner enable="$ctrl.vnApp.loading"></vn-spinner>
|
||||||
<vn-main-menu></vn-main-menu>
|
<vn-main-menu></vn-main-menu>
|
||||||
</vn-topbar>
|
</vn-topbar>
|
||||||
<div ui-view class="main-view">
|
<div ui-view
|
||||||
|
class="main-view"
|
||||||
|
ng-class="{'padding': $ctrl.inApp}">
|
||||||
<vn-home></vn-home>
|
<vn-home></vn-home>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="background"
|
||||||
|
ng-class="{shown: $ctrl.menuShown}"
|
||||||
|
ng-click="$ctrl.hideMenu()">
|
||||||
|
</div>
|
||||||
<vn-snackbar vn-id="snackbar"></vn-snackbar>
|
<vn-snackbar vn-id="snackbar"></vn-snackbar>
|
|
@ -2,15 +2,42 @@ import ngModule from '../../module';
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
|
||||||
export default class App {
|
export default class App {
|
||||||
constructor($scope, vnApp) {
|
constructor($element, $scope, vnApp, $state, $transitions) {
|
||||||
|
this.$element = $element;
|
||||||
this.$ = $scope;
|
this.$ = $scope;
|
||||||
this.vnApp = vnApp;
|
this.vnApp = vnApp;
|
||||||
|
this.$state = $state;
|
||||||
|
|
||||||
|
$transitions.onStart({}, () => {
|
||||||
|
if (this.menuShown) this.hideMenu();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
$postLink() {
|
$postLink() {
|
||||||
|
this.background = this.$element[0].querySelector('.background');
|
||||||
this.vnApp.snackbar = this.$.snackbar;
|
this.vnApp.snackbar = this.$.snackbar;
|
||||||
}
|
}
|
||||||
|
// TODO: Temporary fix to hide the topbar when login is displayed
|
||||||
|
get inApp() {
|
||||||
|
let state = this.$state.current.name;
|
||||||
|
return state && state != 'login';
|
||||||
|
}
|
||||||
|
get leftBlock() {
|
||||||
|
return this.$element[0].querySelector('.left-block');
|
||||||
|
}
|
||||||
|
showMenu() {
|
||||||
|
let leftBlock = this.leftBlock;
|
||||||
|
if (!leftBlock) return;
|
||||||
|
leftBlock.classList.add('shown');
|
||||||
|
this.menuShown = true;
|
||||||
|
}
|
||||||
|
hideMenu() {
|
||||||
|
this.menuShown = false;
|
||||||
|
let leftBlock = this.leftBlock;
|
||||||
|
if (!leftBlock) return;
|
||||||
|
leftBlock.classList.remove('shown');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
App.$inject = ['$scope', 'vnApp'];
|
App.$inject = ['$element', '$scope', 'vnApp', '$state', '$transitions'];
|
||||||
|
|
||||||
ngModule.component('vnApp', {
|
ngModule.component('vnApp', {
|
||||||
template: require('./app.html'),
|
template: require('./app.html'),
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
@import "background";
|
@import "background";
|
||||||
|
|
||||||
|
$menu-width: 16em;
|
||||||
|
$topbar-height: 4em;
|
||||||
|
$mobile-width: 800px;
|
||||||
|
|
||||||
body {
|
body {
|
||||||
@extend .bg-content;
|
@extend .bg-content;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
@ -7,49 +11,104 @@ body {
|
||||||
vn-app {
|
vn-app {
|
||||||
display: block;
|
display: block;
|
||||||
|
|
||||||
vn-topbar {
|
& > vn-topbar {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
z-index: 5;
|
z-index: 2;
|
||||||
box-shadow: 0 .1em .2em rgba(1, 1, 1, .2);
|
box-shadow: 0 .1em .2em rgba(1, 1, 1, .2);
|
||||||
|
height: $topbar-height;
|
||||||
|
padding: .6em;
|
||||||
|
|
||||||
.logo {
|
& > header {
|
||||||
float: left;
|
& > .logo > img {
|
||||||
height: 1.8em;
|
height: 1.8em;
|
||||||
padding: 1em;
|
display: block;
|
||||||
}
|
}
|
||||||
vn-spinner {
|
& > .show-menu {
|
||||||
float: left;
|
display: none;
|
||||||
padding: 1em .4em;
|
font-size: 2.3em;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $main-01;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
& > vn-spinner {
|
||||||
|
padding: 0 .4em;
|
||||||
|
}
|
||||||
|
& > vn-main-menu {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.main-view {
|
& > .main-view {
|
||||||
padding-top: 4em;
|
&.padding {
|
||||||
|
padding-top: $topbar-height;
|
||||||
|
}
|
||||||
vn-main-block {
|
vn-main-block {
|
||||||
display: block;
|
display: block;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding-left: 16em;
|
padding-left: $menu-width;
|
||||||
|
|
||||||
.left-block {
|
.left-block {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: 5;
|
z-index: 5;
|
||||||
top: 4em;
|
top: $topbar-height;
|
||||||
left: 0;
|
left: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
width: 16em;
|
width: $menu-width;
|
||||||
min-width: 16em;
|
min-width: $menu-width;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
box-shadow: 0 .1em .2em rgba(1, 1, 1, .2);
|
box-shadow: 0 .1em .2em rgba(1, 1, 1, .2);
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
.right-block {
|
.right-block {
|
||||||
width: 16em;
|
width: $menu-width;
|
||||||
min-width: 16em;
|
min-width: $menu-width;
|
||||||
padding-left: 1em;
|
padding-left: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
& > .background {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: black;
|
||||||
|
z-index: 4;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 200ms ease-out;
|
||||||
|
}
|
||||||
|
@media screen and (max-width: $mobile-width) {
|
||||||
|
& > vn-topbar > header {
|
||||||
|
& > .logo {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
& > .show-menu {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
& > .main-view vn-main-block {
|
||||||
|
padding-left: 0;
|
||||||
|
|
||||||
|
.left-block {
|
||||||
|
top: 0;
|
||||||
|
transform: translateZ(0) translateX(-$menu-width);
|
||||||
|
transition: transform 200ms ease-out;
|
||||||
|
|
||||||
|
&.shown {
|
||||||
|
transform: translateZ(0) translateX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
& > .background.shown {
|
||||||
|
display: block;
|
||||||
|
opacity: .3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
<div ng-if="$ctrl.$state.current.name === 'home'">
|
<div ng-if="$ctrl.$state.current.name === 'home'">
|
||||||
<div class="modules">
|
<div class="modules">
|
||||||
<a
|
<a
|
||||||
ng-repeat="mod in ::$ctrl.modules"
|
ng-repeat="mod in ::$ctrl.modules"
|
||||||
ui-sref="{{::mod.route.state}}"
|
ui-sref="{{::mod.route.state}}"
|
||||||
translate-attr="::{title: mod.name}"
|
translate-attr="::{title: mod.name}"
|
||||||
class="mdl-shadow--4dp">
|
class="mdl-shadow--4dp">
|
||||||
<div>
|
<div>
|
||||||
<vn-icon icon="{{::mod.icon || 'photo'}}"></vn-icon>
|
<vn-icon icon="{{::mod.icon || 'photo'}}"></vn-icon>
|
||||||
</div>
|
</div>
|
||||||
<h4 ng-bind-html="$ctrl.getModuleName(mod)"></h4>
|
<h4 ng-bind-html="$ctrl.getModuleName(mod)"></h4>
|
||||||
<!--
|
<!--
|
||||||
<span
|
<span
|
||||||
ng-show='mod.keyBind'
|
ng-show='mod.keyBind'
|
||||||
vn-tooltip="Ctrl + Alt + {{mod.keyBind}}">
|
vn-tooltip="Ctrl + Alt + {{mod.keyBind}}">
|
||||||
({{::mod.keyBind}})
|
({{::mod.keyBind}})
|
||||||
</span>
|
</span>
|
||||||
<span ng-show='!mod.keyBind'> </span>
|
<span ng-show='!mod.keyBind'> </span>
|
||||||
-->
|
-->
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,31 +1,19 @@
|
||||||
import ngModule from '../../module';
|
import ngModule from '../../module';
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
import keybindings from '../../global-keybindings.yml';
|
|
||||||
|
|
||||||
export default class Controller {
|
export default class Controller {
|
||||||
constructor(modulesFactory, $state, $translate, $sce) {
|
constructor(vnModules, $state, $translate, $sce) {
|
||||||
this.modules = modulesFactory.getModules();
|
this.modules = vnModules.get();
|
||||||
this.$state = $state;
|
this.$state = $state;
|
||||||
this._ = $translate;
|
this._ = $translate;
|
||||||
this.$sce = $sce;
|
this.$sce = $sce;
|
||||||
this.keybindings = keybindings;
|
|
||||||
}
|
|
||||||
$onInit() {
|
|
||||||
this.modules.map(mod => {
|
|
||||||
let keyBind = this.keybindings.find(keyBind => {
|
|
||||||
return keyBind.sref == mod.route.state;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (keyBind)
|
|
||||||
mod.keyBind = keyBind.key.toUpperCase();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
getModuleName(mod) {
|
getModuleName(mod) {
|
||||||
let getName = mod => {
|
let getName = mod => {
|
||||||
let name = this._.instant(mod.name);
|
let name = this._.instant(mod.name);
|
||||||
let lower = name.toUpperCase();
|
let upper = name.toUpperCase();
|
||||||
if (!mod.keyBind) return name;
|
if (!mod.keyBind) return name;
|
||||||
let index = lower.indexOf(mod.keyBind);
|
let index = upper.indexOf(mod.keyBind);
|
||||||
if (index === -1) return name;
|
if (index === -1) return name;
|
||||||
|
|
||||||
let newName = name.substr(0, index);
|
let newName = name.substr(0, index);
|
||||||
|
@ -38,7 +26,7 @@ export default class Controller {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Controller.$inject = ['modulesFactory', '$state', '$translate', '$sce'];
|
Controller.$inject = ['vnModules', '$state', '$translate', '$sce'];
|
||||||
|
|
||||||
ngModule.component('vnHome', {
|
ngModule.component('vnHome', {
|
||||||
template: require('./home.html'),
|
template: require('./home.html'),
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
@import "effects";
|
@import "effects";
|
||||||
|
|
||||||
vn-home {
|
vn-home {
|
||||||
padding: 2em;
|
display: block;
|
||||||
|
padding: .5em;
|
||||||
|
|
||||||
& > div {
|
& > div {
|
||||||
& > h6 {
|
& > h6 {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import './app/app';
|
import './app/app';
|
||||||
|
import './login/login';
|
||||||
import './home/home';
|
import './home/home';
|
||||||
import './main-menu/main-menu';
|
import './main-menu/main-menu';
|
||||||
import './left-menu/left-menu';
|
import './left-menu/left-menu';
|
||||||
import './topbar/topbar';
|
import './topbar/topbar';
|
||||||
import './user-configuration-popover'
|
import './user-configuration-popover';
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
<div class="box">
|
||||||
|
<img src="./logo.svg"/>
|
||||||
|
<form name="form" ng-submit="$ctrl.submit()">
|
||||||
|
<vn-textfield
|
||||||
|
label="User"
|
||||||
|
model="$ctrl.user"
|
||||||
|
name="user"
|
||||||
|
vn-id="userField"
|
||||||
|
vn-focus>
|
||||||
|
</vn-textfield>
|
||||||
|
<vn-textfield
|
||||||
|
label="Password"
|
||||||
|
model="$ctrl.password"
|
||||||
|
name="password"
|
||||||
|
type="password">
|
||||||
|
</vn-textfield>
|
||||||
|
<vn-check
|
||||||
|
label="Do not close session"
|
||||||
|
field="$ctrl.remember"
|
||||||
|
name="remember">
|
||||||
|
</vn-check>
|
||||||
|
<div class="footer">
|
||||||
|
<vn-submit label="Enter"></vn-submit>
|
||||||
|
<div class="spinner-wrapper">
|
||||||
|
<vn-spinner enable="$ctrl.loading"></vn-spinner>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
|
@ -0,0 +1,38 @@
|
||||||
|
import ngModule from '../../module';
|
||||||
|
import './style.scss';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple login form.
|
||||||
|
*/
|
||||||
|
export default class Controller {
|
||||||
|
constructor($element, $scope, vnAuth) {
|
||||||
|
this.$element = $element;
|
||||||
|
this.$ = $scope;
|
||||||
|
this.vnAuth = vnAuth;
|
||||||
|
this.user = localStorage.getItem('lastUser');
|
||||||
|
}
|
||||||
|
submit() {
|
||||||
|
this.loading = true;
|
||||||
|
this.vnAuth.login(this.user, this.password, this.remember)
|
||||||
|
.then(() => {
|
||||||
|
localStorage.setItem('lastUser', this.user);
|
||||||
|
this.loading = false;
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
this.loading = false;
|
||||||
|
this.password = '';
|
||||||
|
this.focusUser();
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
focusUser() {
|
||||||
|
this.$.userField.select();
|
||||||
|
this.$.userField.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Controller.$inject = ['$element', '$scope', 'vnAuth'];
|
||||||
|
|
||||||
|
ngModule.component('vnLogin', {
|
||||||
|
template: require('./login.html'),
|
||||||
|
controller: Controller
|
||||||
|
});
|
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.2 KiB |
|
@ -0,0 +1,61 @@
|
||||||
|
@import "colors";
|
||||||
|
|
||||||
|
vn-login {
|
||||||
|
position: absolute;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
color: $main-font-color;
|
||||||
|
font-size: 1.1em;
|
||||||
|
font-weight: normal;
|
||||||
|
background-color: #3c393b;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
& > .box {
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: absolute;
|
||||||
|
max-width: 19em;
|
||||||
|
min-width: 15em;
|
||||||
|
padding: 3em;
|
||||||
|
background-color: white;
|
||||||
|
box-shadow: 0 0 1em 0 rgba(1,1,1,.6);
|
||||||
|
border-radius: .5em;
|
||||||
|
|
||||||
|
& > img {
|
||||||
|
width: 100%;
|
||||||
|
padding-bottom: 1em;
|
||||||
|
}
|
||||||
|
& > form {
|
||||||
|
& > vn-textfield {
|
||||||
|
width: 100%;
|
||||||
|
margin: 1em 0;
|
||||||
|
}
|
||||||
|
& > .footer {
|
||||||
|
margin-top: 1em;
|
||||||
|
text-align: center;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
& > .spinner-wrapper {
|
||||||
|
position: absolute;
|
||||||
|
width: 0;
|
||||||
|
top: .3em;
|
||||||
|
right: 3em;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 600px) {
|
||||||
|
background-color: white;
|
||||||
|
|
||||||
|
& > .box {
|
||||||
|
box-shadow: none;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,11 @@
|
||||||
<div style="position: fixed; top: 0; right: 0; padding: .8em 1.5em; z-index: 10;">
|
<div>
|
||||||
<div
|
<div
|
||||||
ng-click="$ctrl.openUserConfiguration($event)"
|
ng-click="$ctrl.openUserConfiguration($event)"
|
||||||
id="user"
|
id="user"
|
||||||
class="unselectable">
|
class="unselectable">
|
||||||
<h6>{{currentUserName}}</h6>
|
{{currentUserName}}
|
||||||
</div>
|
</div>
|
||||||
<a href="/salix/version-notes.html"
|
<a href="version-notes.html"
|
||||||
target="version-notes">
|
target="version-notes">
|
||||||
<vn-icon
|
<vn-icon
|
||||||
id="version-notes"
|
id="version-notes"
|
||||||
|
@ -49,16 +49,8 @@
|
||||||
translate-attr="{title: 'Logout'}"
|
translate-attr="{title: 'Logout'}"
|
||||||
ng-click="$ctrl.onLogoutClick()">
|
ng-click="$ctrl.onLogoutClick()">
|
||||||
</vn-icon>
|
</vn-icon>
|
||||||
|
|
||||||
<!--
|
|
||||||
TODO: Keep it commented until they are functional
|
|
||||||
|
|
||||||
<vn-icon icon="notifications" translate-attr="{title: 'Notifications'}"></vn-icon>
|
|
||||||
<vn-icon icon="account_circle" translate-attr="{title: 'Profile'}"></vn-icon>
|
|
||||||
-->
|
|
||||||
</div>
|
</div>
|
||||||
<vn-popover vn-id="popover">
|
<vn-popover vn-id="popover">
|
||||||
<vn-user-configuration-popover>
|
<vn-user-configuration-popover>
|
||||||
|
|
||||||
</vn-user-configuration-popover>
|
</vn-user-configuration-popover>
|
||||||
</vn-popover>
|
</vn-popover>
|
|
@ -12,13 +12,14 @@ let languages = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class MainMenu {
|
export default class MainMenu {
|
||||||
constructor($translate, $scope, $http, $window, modulesFactory) {
|
constructor($translate, $scope, $http, $window, vnModules, vnAuth) {
|
||||||
this.$ = $scope;
|
this.$ = $scope;
|
||||||
this.$http = $http;
|
this.$http = $http;
|
||||||
this.$translate = $translate;
|
this.$translate = $translate;
|
||||||
this.$window = $window;
|
this.$window = $window;
|
||||||
this.modules = modulesFactory.getModules();
|
this.modules = vnModules.get();
|
||||||
this.langs = [];
|
this.langs = [];
|
||||||
|
this.vnAuth = vnAuth;
|
||||||
|
|
||||||
for (let code of $translate.getAvailableLanguageKeys()) {
|
for (let code of $translate.getAvailableLanguageKeys()) {
|
||||||
this.langs.push({
|
this.langs.push({
|
||||||
|
@ -41,7 +42,7 @@ export default class MainMenu {
|
||||||
}
|
}
|
||||||
|
|
||||||
onLogoutClick() {
|
onLogoutClick() {
|
||||||
this.$window.location = '/logout';
|
this.vnAuth.logout();
|
||||||
}
|
}
|
||||||
|
|
||||||
onChangeLangClick(lang) {
|
onChangeLangClick(lang) {
|
||||||
|
@ -52,7 +53,7 @@ export default class MainMenu {
|
||||||
this.getCurrentUserName();
|
this.getCurrentUserName();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MainMenu.$inject = ['$translate', '$scope', '$http', '$window', 'modulesFactory'];
|
MainMenu.$inject = ['$translate', '$scope', '$http', '$window', 'vnModules', 'vnAuth'];
|
||||||
|
|
||||||
ngModule.component('vnMainMenu', {
|
ngModule.component('vnMainMenu', {
|
||||||
template: require('./main-menu.html'),
|
template: require('./main-menu.html'),
|
||||||
|
|
|
@ -7,9 +7,9 @@ describe('Component vnMainMenu', () => {
|
||||||
beforeEach(ngModule('salix'));
|
beforeEach(ngModule('salix'));
|
||||||
|
|
||||||
beforeEach(angular.mock.inject(($componentController, _$httpBackend_) => {
|
beforeEach(angular.mock.inject(($componentController, _$httpBackend_) => {
|
||||||
let modulesFactory = {getModules: () => {}};
|
let vnModules = {get: () => {}};
|
||||||
$httpBackend = _$httpBackend_;
|
$httpBackend = _$httpBackend_;
|
||||||
controller = $componentController('vnMainMenu', {modulesFactory});
|
controller = $componentController('vnMainMenu', {vnModules});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('getCurrentUserName()', () => {
|
describe('getCurrentUserName()', () => {
|
||||||
|
|
|
@ -1,31 +1,40 @@
|
||||||
@import "effects";
|
@import "effects";
|
||||||
|
|
||||||
vn-main-menu {
|
vn-main-menu {
|
||||||
#user {
|
display: flex;
|
||||||
display: inline-block;
|
align-items: center;
|
||||||
padding-right: 0.2em;
|
justify-content: flex-end;
|
||||||
margin-bottom: 8px;
|
|
||||||
height: 2.5em;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
#user h6{
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
& > div > vn-icon, & > div > a > vn-icon {
|
|
||||||
font-size: 2.2em;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:hover {
|
& > div {
|
||||||
color: $main-01;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
& > * {
|
||||||
|
cursor: pointer;
|
||||||
|
padding-left: .1em;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $main-01;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
& > #user {
|
||||||
|
vertical-align: middle;
|
||||||
|
font-weight: bold;
|
||||||
|
padding-right: .6em;
|
||||||
|
}
|
||||||
|
& > vn-icon,
|
||||||
|
& > a > vn-icon {
|
||||||
|
display: block;
|
||||||
|
font-size: 2.2em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vn-menu.vn-popover > div > div.content > ul {
|
vn-menu.vn-popover > div > div.content > ul {
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: white;
|
color: white;
|
||||||
|
|
||||||
li {
|
& > li {
|
||||||
@extend %clickable-light;
|
@extend %clickable-light;
|
||||||
background-color: $main-01;
|
background-color: $main-01;
|
||||||
margin-bottom: .6em;
|
margin-bottom: .6em;
|
||||||
|
@ -33,13 +42,13 @@ vn-main-menu {
|
||||||
border-radius: .1em;
|
border-radius: .1em;
|
||||||
min-width: 8em;
|
min-width: 8em;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
& > vn-icon {
|
& > vn-icon {
|
||||||
padding-right: .3em;
|
padding-right: .3em;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
&:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,16 @@
|
||||||
header {
|
@import "background";
|
||||||
|
|
||||||
|
vn-topbar {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
color: white;
|
||||||
flex: 1;
|
box-sizing: border-box;
|
||||||
color: #fff;
|
background-color: $bg-dark-bar;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
& > header {
|
||||||
|
height: inherit;
|
||||||
|
width: inherit;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
<header class="bg-dark-bar" ng-transclude>
|
<header ng-transclude>
|
||||||
</header>
|
</header>
|
|
@ -1,78 +0,0 @@
|
||||||
import ngModule from './module';
|
|
||||||
import deps from 'modules.yml';
|
|
||||||
import modules from 'spliting';
|
|
||||||
import splitingRegister from 'core/lib/spliting-register';
|
|
||||||
|
|
||||||
function loader(moduleName, validations) {
|
|
||||||
load.$inject = ['vnModuleLoader'];
|
|
||||||
function load(moduleLoader) {
|
|
||||||
return moduleLoader.load(moduleName, validations);
|
|
||||||
}
|
|
||||||
return load;
|
|
||||||
}
|
|
||||||
|
|
||||||
config.$inject = ['$stateProvider', '$urlRouterProvider', 'aclServiceProvider', 'modulesFactoryProvider'];
|
|
||||||
function config($stateProvider, $urlRouterProvider, aclServiceProvider, modulesFactory) {
|
|
||||||
splitingRegister.graph = deps;
|
|
||||||
splitingRegister.modules = modules;
|
|
||||||
|
|
||||||
let aclService = aclServiceProvider.$get();
|
|
||||||
|
|
||||||
function getParams(route) {
|
|
||||||
let params = '';
|
|
||||||
let temporalParams = [];
|
|
||||||
|
|
||||||
if (!route.params)
|
|
||||||
return params;
|
|
||||||
|
|
||||||
|
|
||||||
Object.keys(route.params).forEach(key => {
|
|
||||||
temporalParams.push(`${key} = "${route.params[key]}"`);
|
|
||||||
});
|
|
||||||
return temporalParams.join(' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
$urlRouterProvider.otherwise('/');
|
|
||||||
|
|
||||||
$stateProvider.state('home', {
|
|
||||||
url: '/',
|
|
||||||
template: '<vn-home></vn-home>',
|
|
||||||
description: 'Salix'
|
|
||||||
});
|
|
||||||
|
|
||||||
for (let file in window.routes) {
|
|
||||||
let fileRoutes = window.routes[file].routes;
|
|
||||||
let moduleName = window.routes[file].module;
|
|
||||||
let validations = window.routes[file].validations || false;
|
|
||||||
let mainModule = modulesFactory.$get().getMainRoute(fileRoutes);
|
|
||||||
|
|
||||||
if (mainModule) {
|
|
||||||
let count = fileRoutes.length;
|
|
||||||
for (let i = 0; i < count; i++) {
|
|
||||||
let route = fileRoutes[i];
|
|
||||||
if (aclService.routeHasPermission(route)) {
|
|
||||||
let configRoute = {
|
|
||||||
url: route.url,
|
|
||||||
template: `<${route.component} ${getParams(route)}></${route.component}>`,
|
|
||||||
description: route.description,
|
|
||||||
reloadOnSearch: false,
|
|
||||||
resolve: {
|
|
||||||
loader: loader(moduleName, validations)
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
moduleIndex: file
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (route.abstract)
|
|
||||||
configRoute.abstract = true;
|
|
||||||
if (route.routeParams)
|
|
||||||
configRoute.params = route.routeParams;
|
|
||||||
|
|
||||||
$stateProvider.state(route.state, configRoute);
|
|
||||||
} else if (route.state === mainModule.state)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ngModule.config(config);
|
|
|
@ -1,5 +1,5 @@
|
||||||
import './module';
|
import './module';
|
||||||
import './config-routes';
|
import './routes';
|
||||||
import './components/index';
|
import './components/index';
|
||||||
|
import './lib/index';
|
||||||
import './styles/index';
|
import './styles/index';
|
||||||
import './modules-factory';
|
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
import ngModule from '../module';
|
||||||
|
import UserError from 'core/lib/user-error';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authentication service.
|
||||||
|
*
|
||||||
|
* @property {Boolean} loggedIn Whether the user is currently logged
|
||||||
|
*/
|
||||||
|
export default class Auth {
|
||||||
|
constructor($http, $state, $transitions, $window, vnToken, vnModules, aclService) {
|
||||||
|
Object.assign(this, {
|
||||||
|
$http,
|
||||||
|
$state,
|
||||||
|
$transitions,
|
||||||
|
$window,
|
||||||
|
vnToken,
|
||||||
|
vnModules,
|
||||||
|
aclService,
|
||||||
|
token: null,
|
||||||
|
loggedIn: false,
|
||||||
|
dataLoaded: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
initialize() {
|
||||||
|
this.loggedIn = this.vnToken.token != null;
|
||||||
|
|
||||||
|
let criteria = {
|
||||||
|
to: state => state.name != 'login'
|
||||||
|
};
|
||||||
|
this.$transitions.onStart(criteria, transition => {
|
||||||
|
if (!this.loggedIn) {
|
||||||
|
let params = {continue: this.$window.location.hash};
|
||||||
|
return transition.router.stateService.target('login', params);
|
||||||
|
}
|
||||||
|
if (!this.dataLoaded) {
|
||||||
|
this.resetData();
|
||||||
|
this.dataLoaded = true;
|
||||||
|
return this.aclService.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
login(user, password, remember) {
|
||||||
|
if (!user)
|
||||||
|
throw new UserError('Please insert your user and password');
|
||||||
|
|
||||||
|
let params = {
|
||||||
|
user,
|
||||||
|
password: password || undefined
|
||||||
|
};
|
||||||
|
return this.$http.post('/api/Accounts/login', params).then(
|
||||||
|
json => this.onLoginOk(json, remember),
|
||||||
|
json => this.onLoginErr(json)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
onLoginOk(json, remember) {
|
||||||
|
this.resetData();
|
||||||
|
this.vnToken.set(json.data.token, remember);
|
||||||
|
this.loggedIn = true;
|
||||||
|
|
||||||
|
let continueHash = this.$state.params.continue;
|
||||||
|
|
||||||
|
if (continueHash)
|
||||||
|
this.$window.location = continueHash;
|
||||||
|
else
|
||||||
|
this.$state.go('home');
|
||||||
|
}
|
||||||
|
onLoginErr(json) {
|
||||||
|
let message;
|
||||||
|
|
||||||
|
switch (json.status) {
|
||||||
|
case 401:
|
||||||
|
message = 'Invalid credentials';
|
||||||
|
break;
|
||||||
|
case -1:
|
||||||
|
message = 'Can\'t contact with server';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
message = 'Something went wrong';
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new UserError(message);
|
||||||
|
}
|
||||||
|
logout() {
|
||||||
|
let promise = this.$http.post('/api/Accounts/logout', null, {
|
||||||
|
headers: {Authorization: this.vnToken.token}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.vnToken.unset();
|
||||||
|
this.loggedIn = false;
|
||||||
|
this.resetData();
|
||||||
|
this.$state.go('login');
|
||||||
|
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
resetData() {
|
||||||
|
this.aclService.reset();
|
||||||
|
this.vnModules.reset();
|
||||||
|
this.dataLoaded = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Auth.$inject = ['$http', '$state', '$transitions', '$window', 'vnToken', 'vnModules', 'aclService'];
|
||||||
|
|
||||||
|
ngModule.service('vnAuth', Auth);
|
|
@ -0,0 +1,4 @@
|
||||||
|
import './auth';
|
||||||
|
import './token';
|
||||||
|
import './modules';
|
||||||
|
import './interceptor';
|
|
@ -1,16 +1,14 @@
|
||||||
import ngModule from '../module';
|
import ngModule from '../module';
|
||||||
import HttpError from './http-error';
|
import HttpError from 'core/lib/http-error';
|
||||||
|
|
||||||
interceptor.$inject = ['$q', 'vnApp', '$cookies', '$translate'];
|
interceptor.$inject = ['$q', 'vnApp', 'vnToken', '$translate'];
|
||||||
function interceptor($q, vnApp, $cookies, $translate) {
|
function interceptor($q, vnApp, vnToken, $translate) {
|
||||||
return {
|
return {
|
||||||
request: function(config) {
|
request: function(config) {
|
||||||
vnApp.pushLoader();
|
vnApp.pushLoader();
|
||||||
let token = $cookies.get('vnToken');
|
|
||||||
|
|
||||||
if (token)
|
|
||||||
config.headers.Authorization = token;
|
|
||||||
|
|
||||||
|
if (vnToken.token)
|
||||||
|
config.headers.Authorization = vnToken.token;
|
||||||
if ($translate.use())
|
if ($translate.use())
|
||||||
config.headers['Accept-Language'] = $translate.use();
|
config.headers['Accept-Language'] = $translate.use();
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
import ngModule from '../module';
|
||||||
|
import {getMainRoute} from '../routes';
|
||||||
|
import keybindings from '../keybindings.yml';
|
||||||
|
|
||||||
|
export default class Modules {
|
||||||
|
constructor(aclService, $window) {
|
||||||
|
Object.assign(this, {
|
||||||
|
aclService,
|
||||||
|
$window
|
||||||
|
});
|
||||||
|
}
|
||||||
|
reset() {
|
||||||
|
this.modules = null;
|
||||||
|
}
|
||||||
|
get() {
|
||||||
|
if (this.modules)
|
||||||
|
return this.modules;
|
||||||
|
|
||||||
|
this.modules = [];
|
||||||
|
for (let mod of this.$window.routes) {
|
||||||
|
if (!mod || !mod.routes) continue;
|
||||||
|
|
||||||
|
let route = getMainRoute(mod.routes);
|
||||||
|
if (!route || (route.acl && !this.aclService.hasAny(route.acl)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
let keyBind;
|
||||||
|
if (keybindings) {
|
||||||
|
let res = keybindings.find(i => i.sref == route.state);
|
||||||
|
if (res) keyBind = res.key.toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.modules.push({
|
||||||
|
name: mod.name || mod.module,
|
||||||
|
icon: mod.icon || null,
|
||||||
|
route,
|
||||||
|
keyBind
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.modules;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Modules.$inject = ['aclService', '$window'];
|
||||||
|
|
||||||
|
ngModule.service('vnModules', Modules);
|
|
@ -0,0 +1,51 @@
|
||||||
|
import ngModule from '../module';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves and loads the token for the current logged in user.
|
||||||
|
*
|
||||||
|
* @property {String} token The current login token or %null
|
||||||
|
*/
|
||||||
|
export default class Token {
|
||||||
|
constructor($cookies) {
|
||||||
|
this.$cookies = $cookies;
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.token = sessionStorage.getItem('vnToken');
|
||||||
|
if (!this.token)
|
||||||
|
this.token = localStorage.getItem('vnToken');
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
if (!this.token)
|
||||||
|
this.token = this.$cookies.get('vnToken');
|
||||||
|
}
|
||||||
|
set(value, remember) {
|
||||||
|
this.unset();
|
||||||
|
try {
|
||||||
|
if (remember)
|
||||||
|
localStorage.setItem('vnToken', value);
|
||||||
|
else
|
||||||
|
sessionStorage.setItem('vnToken', value);
|
||||||
|
} catch (e) {
|
||||||
|
let options = {};
|
||||||
|
|
||||||
|
if (location.protocol == 'https:')
|
||||||
|
options.secure = true;
|
||||||
|
if (remember) {
|
||||||
|
let now = new Date().getTime();
|
||||||
|
options.expires = new Date(now + 7 * 86400000);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$cookies.put('vnToken', value, options);
|
||||||
|
}
|
||||||
|
this.token = value;
|
||||||
|
}
|
||||||
|
unset() {
|
||||||
|
localStorage.removeItem('vnToken');
|
||||||
|
sessionStorage.removeItem('vnToken');
|
||||||
|
this.$cookies.remove('vnToken');
|
||||||
|
this.token = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Token.$inject = ['$cookies'];
|
||||||
|
|
||||||
|
ngModule.service('vnToken', Token);
|
|
@ -1,17 +1,19 @@
|
||||||
import {ng} from 'core/vendor';
|
import {ng} from 'core/vendor';
|
||||||
import 'core';
|
import 'core';
|
||||||
import keybindings from './global-keybindings.yml';
|
import keybindings from './keybindings.yml';
|
||||||
|
|
||||||
export const appName = 'salix';
|
export const appName = 'salix';
|
||||||
|
|
||||||
const ngModule = ng.module('salix', ['vnCore']);
|
const ngModule = ng.module('salix', ['vnCore']);
|
||||||
export default ngModule;
|
export default ngModule;
|
||||||
|
|
||||||
run.$inject = ['$window', '$rootScope', 'vnApp', '$state', '$document'];
|
run.$inject = ['$window', '$rootScope', 'vnAuth', 'vnApp', '$state', '$document'];
|
||||||
export function run($window, $rootScope, vnApp, $state, $document) {
|
export function run($window, $rootScope, vnAuth, vnApp, $state, $document) {
|
||||||
$window.validations = {};
|
$window.validations = {};
|
||||||
vnApp.name = appName;
|
vnApp.name = appName;
|
||||||
|
|
||||||
|
vnAuth.initialize();
|
||||||
|
|
||||||
$rootScope.$on('$viewContentLoaded', () => {});
|
$rootScope.$on('$viewContentLoaded', () => {});
|
||||||
window.myAppErrorLog = [];
|
window.myAppErrorLog = [];
|
||||||
$state.defaultErrorHandler(function(error) {
|
$state.defaultErrorHandler(function(error) {
|
||||||
|
@ -40,7 +42,6 @@ export function run($window, $rootScope, vnApp, $state, $document) {
|
||||||
if (correctShortcut) {
|
if (correctShortcut) {
|
||||||
$state.go(keybindings[binding].sref);
|
$state.go(keybindings[binding].sref);
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopImmediatePropagation();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -56,8 +57,8 @@ ngModule.config(config);
|
||||||
|
|
||||||
// Unhandled exceptions
|
// Unhandled exceptions
|
||||||
|
|
||||||
$exceptionHandler.$inject = ['vnApp', '$window'];
|
$exceptionHandler.$inject = ['vnApp', '$window', '$state'];
|
||||||
function $exceptionHandler(vnApp, $window) {
|
function $exceptionHandler(vnApp, $window, $state) {
|
||||||
return function(exception, cause) {
|
return function(exception, cause) {
|
||||||
let message;
|
let message;
|
||||||
|
|
||||||
|
@ -77,11 +78,9 @@ function $exceptionHandler(vnApp, $window) {
|
||||||
else
|
else
|
||||||
message = `${exception.status}: ${exception.statusText}`;
|
message = `${exception.status}: ${exception.statusText}`;
|
||||||
|
|
||||||
if (exception.status === 401) {
|
if (exception.status === 401 && $state.current.name != 'login') {
|
||||||
let location = $window.location;
|
let params = {continue: $window.location.hash};
|
||||||
let continueUrl = location.pathname + location.search + location.hash;
|
$state.go('login', params);
|
||||||
continueUrl = encodeURIComponent(continueUrl);
|
|
||||||
$window.location = `/auth/?apiKey=${vnApp.name}&continue=${continueUrl}`;
|
|
||||||
}
|
}
|
||||||
} else if (exception.name == 'UserError')
|
} else if (exception.name == 'UserError')
|
||||||
message = exception.message;
|
message = exception.message;
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
import ngModule from './module';
|
|
||||||
|
|
||||||
function modulesFactory(aclService) {
|
|
||||||
function getMainRoute(routeCollection) {
|
|
||||||
let cant = routeCollection.length;
|
|
||||||
for (let i = 0; i < cant; i++) {
|
|
||||||
if (!routeCollection[i].abstract)
|
|
||||||
return routeCollection[i];
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getModules() {
|
|
||||||
let modules = [];
|
|
||||||
for (let file in window.routes) {
|
|
||||||
let card = {
|
|
||||||
name: routes[file].name || routes[file].module,
|
|
||||||
icon: routes[file].icon || null
|
|
||||||
};
|
|
||||||
let mainRoute = getMainRoute(window.routes[file].routes);
|
|
||||||
if (mainRoute && aclService.routeHasPermission(mainRoute)) {
|
|
||||||
card.route = mainRoute;
|
|
||||||
modules.push(card);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return modules;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
getModules: getModules,
|
|
||||||
getMainRoute: getMainRoute
|
|
||||||
};
|
|
||||||
}
|
|
||||||
modulesFactory.$inject = ['aclService'];
|
|
||||||
|
|
||||||
ngModule.factory('modulesFactory', modulesFactory);
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
import ngModule from './module';
|
||||||
|
import deps from 'modules.yml';
|
||||||
|
import modules from 'spliting';
|
||||||
|
import splitingRegister from 'core/lib/spliting-register';
|
||||||
|
|
||||||
|
export function getMainRoute(routes) {
|
||||||
|
for (let route of routes) {
|
||||||
|
if (!route.abstract)
|
||||||
|
return route;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function loader(moduleName, validations) {
|
||||||
|
load.$inject = ['vnModuleLoader'];
|
||||||
|
function load(moduleLoader) {
|
||||||
|
return moduleLoader.load(moduleName, validations);
|
||||||
|
}
|
||||||
|
return load;
|
||||||
|
}
|
||||||
|
|
||||||
|
config.$inject = ['$stateProvider', '$urlRouterProvider'];
|
||||||
|
function config($stateProvider, $urlRouterProvider) {
|
||||||
|
splitingRegister.graph = deps;
|
||||||
|
splitingRegister.modules = modules;
|
||||||
|
|
||||||
|
$urlRouterProvider.otherwise('/');
|
||||||
|
|
||||||
|
$stateProvider.state('home', {
|
||||||
|
url: '/',
|
||||||
|
template: '<vn-home></vn-home>',
|
||||||
|
description: 'Salix'
|
||||||
|
});
|
||||||
|
$stateProvider.state('login', {
|
||||||
|
url: '/login?continue',
|
||||||
|
template: '<vn-login></vn-login>',
|
||||||
|
description: 'Login'
|
||||||
|
});
|
||||||
|
|
||||||
|
function getParams(route) {
|
||||||
|
let params = '';
|
||||||
|
let temporalParams = [];
|
||||||
|
|
||||||
|
if (!route.params)
|
||||||
|
return params;
|
||||||
|
|
||||||
|
|
||||||
|
Object.keys(route.params).forEach(key => {
|
||||||
|
temporalParams.push(`${key} = "${route.params[key]}"`);
|
||||||
|
});
|
||||||
|
return temporalParams.join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let file in window.routes) {
|
||||||
|
let routeFile = window.routes[file];
|
||||||
|
let fileRoutes = routeFile.routes;
|
||||||
|
let moduleName = routeFile.module;
|
||||||
|
let validations = routeFile.validations || false;
|
||||||
|
let mainModule = getMainRoute(fileRoutes);
|
||||||
|
|
||||||
|
if (!mainModule) continue;
|
||||||
|
|
||||||
|
for (let route of fileRoutes) {
|
||||||
|
let configRoute = {
|
||||||
|
url: route.url,
|
||||||
|
template: `<${route.component} ${getParams(route)}></${route.component}>`,
|
||||||
|
description: route.description,
|
||||||
|
reloadOnSearch: false,
|
||||||
|
resolve: {
|
||||||
|
loader: loader(moduleName, validations)
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
moduleIndex: file
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (route.abstract)
|
||||||
|
configRoute.abstract = true;
|
||||||
|
if (route.routeParams)
|
||||||
|
configRoute.params = route.routeParams;
|
||||||
|
|
||||||
|
$stateProvider.state(route.state, configRoute);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ngModule.config(config);
|
|
@ -1,13 +0,0 @@
|
||||||
<!doctype html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Salix</title>
|
|
||||||
</head>
|
|
||||||
<body ng-app="vnAuth">
|
|
||||||
<vn-login></vn-login>
|
|
||||||
<% for (let jsFile of assets) { %>
|
|
||||||
<script type="text/javascript" src="<%= jsFile %>"></script>
|
|
||||||
<% } %>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -3,7 +3,6 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title vn-title translate></title>
|
<title vn-title translate></title>
|
||||||
<script src="/acl"></script>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<vn-app></vn-app>
|
<vn-app></vn-app>
|
||||||
|
|
|
@ -1,94 +0,0 @@
|
||||||
let url = require('url');
|
|
||||||
let md5 = require('md5');
|
|
||||||
|
|
||||||
module.exports = function(app) {
|
|
||||||
let User = app.models.User;
|
|
||||||
let applications = app.get('applications');
|
|
||||||
|
|
||||||
app.get('/auth/', function(req, res) {
|
|
||||||
res.render('auth.ejs', {
|
|
||||||
assets: app.getWpAssets('auth')
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
app.post('/auth/login', function(req, res) {
|
|
||||||
let body = req.body;
|
|
||||||
let user = body.user;
|
|
||||||
let password = body.password;
|
|
||||||
let syncOnFail = true;
|
|
||||||
let usesEmail = user && user.indexOf('@') !== -1;
|
|
||||||
|
|
||||||
login();
|
|
||||||
|
|
||||||
function login() {
|
|
||||||
let loginInfo = {password: password};
|
|
||||||
|
|
||||||
if (usesEmail)
|
|
||||||
loginInfo.email = user;
|
|
||||||
else
|
|
||||||
loginInfo.username = user;
|
|
||||||
|
|
||||||
User.login(loginInfo, 'user', loginCb);
|
|
||||||
}
|
|
||||||
function loginCb(err, token) {
|
|
||||||
if (err) {
|
|
||||||
if (syncOnFail && !usesEmail) {
|
|
||||||
syncOnFail = false;
|
|
||||||
let filter = {where: {name: user}};
|
|
||||||
app.models.Account.findOne(filter, findCb);
|
|
||||||
} else
|
|
||||||
badLogin();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let apiKey;
|
|
||||||
let continueUrl;
|
|
||||||
|
|
||||||
try {
|
|
||||||
let query = url.parse(req.body.location, true).query;
|
|
||||||
apiKey = query.apiKey;
|
|
||||||
continueUrl = query.continue;
|
|
||||||
} catch (e) {
|
|
||||||
continueUrl = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!apiKey) apiKey = 'default';
|
|
||||||
let loginUrl = applications[apiKey] || '/login';
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
token: token.id,
|
|
||||||
continue: continueUrl,
|
|
||||||
loginUrl: loginUrl
|
|
||||||
});
|
|
||||||
}
|
|
||||||
function findCb(err, instance) {
|
|
||||||
if (err || !instance || instance.password !== md5(password)) {
|
|
||||||
badLogin();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let where = {id: instance.id};
|
|
||||||
let userData = {
|
|
||||||
id: instance.id,
|
|
||||||
username: user,
|
|
||||||
password: password,
|
|
||||||
email: instance.email,
|
|
||||||
created: instance.created,
|
|
||||||
updated: instance.updated
|
|
||||||
};
|
|
||||||
User.upsertWithWhere(where, userData, login);
|
|
||||||
}
|
|
||||||
function badLogin() {
|
|
||||||
res.status(401);
|
|
||||||
res.json({
|
|
||||||
message: 'Login failed'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
app.get('/auth/logout', function(req, res) {
|
|
||||||
User.logout(req.accessToken.id, () => {
|
|
||||||
res.redirect('/');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -1,146 +1,10 @@
|
||||||
module.exports = function(app) {
|
module.exports = function(app) {
|
||||||
let models = app.models;
|
|
||||||
let bootTimestamp = new Date().getTime();
|
let bootTimestamp = new Date().getTime();
|
||||||
|
|
||||||
app.get('/', function(req, res) {
|
app.get('/', function(req, res) {
|
||||||
let token = req.cookies.vnToken;
|
res.render('index.ejs', {
|
||||||
validateToken(token, function(isValid) {
|
assets: app.getWpAssets('salix'),
|
||||||
if (!isValid) {
|
version: bootTimestamp
|
||||||
redirectToAuth(res, req.get('origin'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
res.render('index.ejs', {
|
|
||||||
assets: app.getWpAssets('salix'),
|
|
||||||
version: bootTimestamp
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('/acl', function(req, res) {
|
|
||||||
let token = req.cookies.vnToken;
|
|
||||||
validateToken(token, function(isValid, token) {
|
|
||||||
if (isValid)
|
|
||||||
sendUserRole(res, token);
|
|
||||||
else
|
|
||||||
sendACL(res, {});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
app.get('/login', function(req, res) {
|
|
||||||
let token = req.query.token;
|
|
||||||
let continueUrl = req.query.continue;
|
|
||||||
|
|
||||||
validateToken(token, function(isValid) {
|
|
||||||
if (isValid) {
|
|
||||||
res.cookie('vnToken', token /* , {httpOnly: true} */);
|
|
||||||
res.redirect(continueUrl ? continueUrl : '/');
|
|
||||||
} else
|
|
||||||
redirectToAuth(res);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
app.get('/logout', function(req, res) {
|
|
||||||
let token = req.cookies.vnToken;
|
|
||||||
models.User.logout(token, function() {
|
|
||||||
redirectToAuth(res);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
app.get('/validateToken', function(req, res) {
|
|
||||||
let token = req.headers.authorization;
|
|
||||||
|
|
||||||
validateToken(token, function(isValid) {
|
|
||||||
if (isValid)
|
|
||||||
res.json(null);
|
|
||||||
else {
|
|
||||||
res.status(401).json({
|
|
||||||
message: 'Invalid token'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function validateToken(tokenId, cb) {
|
|
||||||
models.AccessToken.findById(tokenId, function(err, token) {
|
|
||||||
if (token) {
|
|
||||||
token.validate(function(err, isValid) {
|
|
||||||
cb(isValid === true, token);
|
|
||||||
});
|
|
||||||
} else
|
|
||||||
cb(false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function sendUserRole(res, token) {
|
|
||||||
if (token.userId) {
|
|
||||||
let query = {
|
|
||||||
where: {
|
|
||||||
principalId: token.userId,
|
|
||||||
principalType: 'USER'
|
|
||||||
},
|
|
||||||
include: [{
|
|
||||||
relation: 'role',
|
|
||||||
scope: {
|
|
||||||
fields: ['name']
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
models.RoleMapping.find(query, function(_, roles) {
|
|
||||||
if (roles) {
|
|
||||||
let acl = {
|
|
||||||
userProfile: {},
|
|
||||||
roles: {}
|
|
||||||
};
|
|
||||||
Object.keys(roles).forEach(function(_, i) {
|
|
||||||
if (roles[i].roleId) {
|
|
||||||
let rol = roles[i].role();
|
|
||||||
acl.roles[rol.name] = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
models.User.findById(token.userId, function(_, userProfile) {
|
|
||||||
// acl.userProfile = userProfile;
|
|
||||||
if (userProfile && userProfile.id) {
|
|
||||||
acl.userProfile.id = userProfile.id;
|
|
||||||
acl.userProfile.username = userProfile.username;
|
|
||||||
acl.userProfile.warehouseId = 1;
|
|
||||||
sendACL(res, acl);
|
|
||||||
} else
|
|
||||||
sendACL(res, {});
|
|
||||||
});
|
|
||||||
} else
|
|
||||||
sendACL(res, {});
|
|
||||||
});
|
|
||||||
} else
|
|
||||||
sendACL(res, {});
|
|
||||||
}
|
|
||||||
|
|
||||||
function redirectToAuth(res, continueUrl) {
|
|
||||||
let authUrl = app.get('url auth');
|
|
||||||
let params = {
|
|
||||||
apiKey: app.get('api key'),
|
|
||||||
continue: continueUrl
|
|
||||||
};
|
|
||||||
res.clearCookie('vnToken');
|
|
||||||
res.redirect(`${authUrl}/?${encodeUri(params)}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
function sendACL(res, acl) {
|
|
||||||
let aclStr = JSON.stringify(acl);
|
|
||||||
res.header('Content-Type', 'application/javascript; charset=UTF-8');
|
|
||||||
res.send(`(function(window){window.salix = window.salix || {}; window.salix.acl = window.salix.acl || {}; window.salix.acl = ${aclStr}; })(window)`);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function encodeUri(object) {
|
|
||||||
let uri = '';
|
|
||||||
for (let key in object) {
|
|
||||||
if (object[key]) {
|
|
||||||
if (uri.length > 0)
|
|
||||||
uri += '&';
|
|
||||||
uri += encodeURIComponent(key) + '=';
|
|
||||||
uri += encodeURIComponent(object[key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return uri;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,103 +0,0 @@
|
||||||
const app = require('../../../server/server');
|
|
||||||
const routes = require('../auth');
|
|
||||||
|
|
||||||
describe('Auth routes', () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
await app.models.User.destroyById(102);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(async () => {
|
|
||||||
await app.models.User.destroyById(102);
|
|
||||||
});
|
|
||||||
|
|
||||||
let User = app.models.User;
|
|
||||||
let loginFunction;
|
|
||||||
let logoutFunction;
|
|
||||||
let res;
|
|
||||||
let req;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
spyOn(app, 'post');
|
|
||||||
spyOn(app, 'get').and.callThrough();
|
|
||||||
routes(app);
|
|
||||||
loginFunction = app.post.calls.mostRecent().args[1];
|
|
||||||
logoutFunction = app.get.calls.argsFor(2)[1];
|
|
||||||
res = {};
|
|
||||||
req = {body: {}};
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when the user doesnt exist but the client does and the password is correct', () => {
|
|
||||||
it('should create the user login and return the token', done => {
|
|
||||||
spyOn(User, 'upsertWithWhere').and.callThrough();
|
|
||||||
req.body.user = 'PetterParker';
|
|
||||||
req.body.password = 'nightmare';
|
|
||||||
res.json = response => {
|
|
||||||
expect(User.upsertWithWhere).toHaveBeenCalledWith(jasmine.any(Object), jasmine.any(Object), jasmine.any(Function));
|
|
||||||
expect(response.token).toBeDefined();
|
|
||||||
done();
|
|
||||||
};
|
|
||||||
loginFunction(req, res);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when the user exists and the password is correct', () => {
|
|
||||||
it('should login and return the token', done => {
|
|
||||||
req.body.user = 'employee';
|
|
||||||
req.body.password = 'nightmare';
|
|
||||||
res.json = response => {
|
|
||||||
expect(response.token).toBeDefined();
|
|
||||||
done();
|
|
||||||
};
|
|
||||||
loginFunction(req, res);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should define the url to continue upon login', done => {
|
|
||||||
req.body.user = 'employee';
|
|
||||||
req.body.password = 'nightmare';
|
|
||||||
req.body.location = 'http://localhost:5000/auth/?apiKey=salix&continue="continueURL"';
|
|
||||||
res.json = response => {
|
|
||||||
expect(response.continue).toBeDefined();
|
|
||||||
done();
|
|
||||||
};
|
|
||||||
loginFunction(req, res);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should define the loginUrl upon login', done => {
|
|
||||||
req.body.user = 'employee';
|
|
||||||
req.body.password = 'nightmare';
|
|
||||||
req.body.location = 'http://localhost:5000/auth/?apiKey=salix';
|
|
||||||
res.json = response => {
|
|
||||||
expect(response.loginUrl).toBeDefined();
|
|
||||||
done();
|
|
||||||
};
|
|
||||||
loginFunction(req, res);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should logout after login', done => {
|
|
||||||
spyOn(User, 'logout').and.callThrough();
|
|
||||||
req.accessToken = {id: 'testingTokenId'};
|
|
||||||
logoutFunction(req, res);
|
|
||||||
res.redirect = url => {
|
|
||||||
expect(User.logout).toHaveBeenCalledWith('testingTokenId', jasmine.any(Function));
|
|
||||||
expect(url).toBe('/');
|
|
||||||
done();
|
|
||||||
};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when the user is incorrect', () => {
|
|
||||||
it('should return a 401 unauthorized', done => {
|
|
||||||
req.body.user = 'IDontExist';
|
|
||||||
req.body.password = 'TotallyWrongPassword';
|
|
||||||
res.status = status => {
|
|
||||||
expect(status).toBe(401);
|
|
||||||
};
|
|
||||||
|
|
||||||
res.json = response => {
|
|
||||||
expect(response.message).toBe('Login failed');
|
|
||||||
done();
|
|
||||||
};
|
|
||||||
loginFunction(req, res);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -2,12 +2,12 @@ import ngModule from '../../module';
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
|
||||||
class Controller {
|
class Controller {
|
||||||
constructor($stateParams, $translate, $scope, $cookies) {
|
constructor($stateParams, $translate, $scope, vnToken) {
|
||||||
this.$ = $scope;
|
this.$ = $scope;
|
||||||
this.$stateParams = $stateParams;
|
this.$stateParams = $stateParams;
|
||||||
this.$translate = $translate;
|
this.$translate = $translate;
|
||||||
|
|
||||||
this.accessToken = $cookies.get('vnToken');
|
this.accessToken = vnToken.token;
|
||||||
this.companyFk = window.localStorage.defaultCompanyFk;
|
this.companyFk = window.localStorage.defaultCompanyFk;
|
||||||
this.filter = {
|
this.filter = {
|
||||||
include: {
|
include: {
|
||||||
|
@ -57,7 +57,7 @@ class Controller {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Controller.$inject = ['$stateParams', '$translate', '$scope', '$cookies'];
|
Controller.$inject = ['$stateParams', '$translate', '$scope', 'vnToken'];
|
||||||
|
|
||||||
ngModule.component('vnClientRiskIndex', {
|
ngModule.component('vnClientRiskIndex', {
|
||||||
template: require('./index.html'),
|
template: require('./index.html'),
|
||||||
|
|
|
@ -1271,7 +1271,7 @@
|
||||||
},
|
},
|
||||||
"ansi-escapes": {
|
"ansi-escapes": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz",
|
"resolved": "http://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz",
|
||||||
"integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==",
|
"integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
@ -1564,7 +1564,7 @@
|
||||||
},
|
},
|
||||||
"util": {
|
"util": {
|
||||||
"version": "0.10.3",
|
"version": "0.10.3",
|
||||||
"resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz",
|
"resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz",
|
||||||
"integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
|
"integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
@ -1883,7 +1883,7 @@
|
||||||
"base": {
|
"base": {
|
||||||
"version": "0.11.2",
|
"version": "0.11.2",
|
||||||
"resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
|
"resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
|
||||||
"integrity": "sha1-e95c7RRbbVUakNuH+DxVi060io8=",
|
"integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"cache-base": "^1.0.1",
|
"cache-base": "^1.0.1",
|
||||||
|
@ -1963,6 +1963,436 @@
|
||||||
"resolved": "https://registry.npmjs.org/bcp47/-/bcp47-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/bcp47/-/bcp47-1.1.2.tgz",
|
||||||
"integrity": "sha1-NUvjMH/9CEM6ePXh4glYRfifx/4="
|
"integrity": "sha1-NUvjMH/9CEM6ePXh4glYRfifx/4="
|
||||||
},
|
},
|
||||||
|
"bcrypt": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-4EuzUo6K790QC3uq/ogzy9w2Hc7XDIBoEndU5y7l7YaEAwQF8vyFqv6tC30+gOBZvyxk3F632xzKBQoLNz2pjg==",
|
||||||
|
"requires": {
|
||||||
|
"nan": "2.12.1",
|
||||||
|
"node-pre-gyp": "0.12.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"abbrev": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"bundled": true
|
||||||
|
},
|
||||||
|
"ansi-regex": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"bundled": true
|
||||||
|
},
|
||||||
|
"aproba": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"bundled": true
|
||||||
|
},
|
||||||
|
"are-we-there-yet": {
|
||||||
|
"version": "1.1.5",
|
||||||
|
"bundled": true,
|
||||||
|
"requires": {
|
||||||
|
"delegates": "^1.0.0",
|
||||||
|
"readable-stream": "^2.0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"balanced-match": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"bundled": true
|
||||||
|
},
|
||||||
|
"brace-expansion": {
|
||||||
|
"version": "1.1.11",
|
||||||
|
"bundled": true,
|
||||||
|
"requires": {
|
||||||
|
"balanced-match": "^1.0.0",
|
||||||
|
"concat-map": "0.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"chownr": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"bundled": true
|
||||||
|
},
|
||||||
|
"code-point-at": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"bundled": true
|
||||||
|
},
|
||||||
|
"concat-map": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"bundled": true
|
||||||
|
},
|
||||||
|
"console-control-strings": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"bundled": true
|
||||||
|
},
|
||||||
|
"core-util-is": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"bundled": true
|
||||||
|
},
|
||||||
|
"debug": {
|
||||||
|
"version": "2.6.9",
|
||||||
|
"bundled": true,
|
||||||
|
"requires": {
|
||||||
|
"ms": "2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deep-extend": {
|
||||||
|
"version": "0.6.0",
|
||||||
|
"bundled": true
|
||||||
|
},
|
||||||
|
"delegates": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"bundled": true
|
||||||
|
},
|
||||||
|
"detect-libc": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"bundled": true
|
||||||
|
},
|
||||||
|
"fs-minipass": {
|
||||||
|
"version": "1.2.5",
|
||||||
|
"bundled": true,
|
||||||
|
"requires": {
|
||||||
|
"minipass": "^2.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fs.realpath": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"bundled": true
|
||||||
|
},
|
||||||
|
"gauge": {
|
||||||
|
"version": "2.7.4",
|
||||||
|
"bundled": true,
|
||||||
|
"requires": {
|
||||||
|
"aproba": "^1.0.3",
|
||||||
|
"console-control-strings": "^1.0.0",
|
||||||
|
"has-unicode": "^2.0.0",
|
||||||
|
"object-assign": "^4.1.0",
|
||||||
|
"signal-exit": "^3.0.0",
|
||||||
|
"string-width": "^1.0.1",
|
||||||
|
"strip-ansi": "^3.0.1",
|
||||||
|
"wide-align": "^1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"glob": {
|
||||||
|
"version": "7.1.2",
|
||||||
|
"bundled": true,
|
||||||
|
"requires": {
|
||||||
|
"fs.realpath": "^1.0.0",
|
||||||
|
"inflight": "^1.0.4",
|
||||||
|
"inherits": "2",
|
||||||
|
"minimatch": "^3.0.4",
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"path-is-absolute": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"has-unicode": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"bundled": true
|
||||||
|
},
|
||||||
|
"iconv-lite": {
|
||||||
|
"version": "0.4.24",
|
||||||
|
"bundled": true,
|
||||||
|
"requires": {
|
||||||
|
"safer-buffer": ">= 2.1.2 < 3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ignore-walk": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"bundled": true,
|
||||||
|
"requires": {
|
||||||
|
"minimatch": "^3.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"inflight": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"bundled": true,
|
||||||
|
"requires": {
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"wrappy": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"inherits": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"bundled": true
|
||||||
|
},
|
||||||
|
"ini": {
|
||||||
|
"version": "1.3.5",
|
||||||
|
"bundled": true
|
||||||
|
},
|
||||||
|
"is-fullwidth-code-point": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"bundled": true,
|
||||||
|
"requires": {
|
||||||
|
"number-is-nan": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isarray": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"bundled": true
|
||||||
|
},
|
||||||
|
"minimatch": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"bundled": true,
|
||||||
|
"requires": {
|
||||||
|
"brace-expansion": "^1.1.7"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minimist": {
|
||||||
|
"version": "0.0.8",
|
||||||
|
"bundled": true
|
||||||
|
},
|
||||||
|
"minipass": {
|
||||||
|
"version": "2.3.4",
|
||||||
|
"bundled": true,
|
||||||
|
"requires": {
|
||||||
|
"safe-buffer": "^5.1.2",
|
||||||
|
"yallist": "^3.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"safe-buffer": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"bundled": true
|
||||||
|
},
|
||||||
|
"yallist": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"bundled": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minizlib": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"bundled": true,
|
||||||
|
"requires": {
|
||||||
|
"minipass": "^2.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mkdirp": {
|
||||||
|
"version": "0.5.1",
|
||||||
|
"bundled": true,
|
||||||
|
"requires": {
|
||||||
|
"minimist": "0.0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"bundled": true
|
||||||
|
},
|
||||||
|
"needle": {
|
||||||
|
"version": "2.2.4",
|
||||||
|
"bundled": true,
|
||||||
|
"requires": {
|
||||||
|
"debug": "^2.1.2",
|
||||||
|
"iconv-lite": "^0.4.4",
|
||||||
|
"sax": "^1.2.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node-pre-gyp": {
|
||||||
|
"version": "0.12.0",
|
||||||
|
"bundled": true,
|
||||||
|
"requires": {
|
||||||
|
"detect-libc": "^1.0.2",
|
||||||
|
"mkdirp": "^0.5.1",
|
||||||
|
"needle": "^2.2.1",
|
||||||
|
"nopt": "^4.0.1",
|
||||||
|
"npm-packlist": "^1.1.6",
|
||||||
|
"npmlog": "^4.0.2",
|
||||||
|
"rc": "^1.2.7",
|
||||||
|
"rimraf": "^2.6.1",
|
||||||
|
"semver": "^5.3.0",
|
||||||
|
"tar": "^4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nopt": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"bundled": true,
|
||||||
|
"requires": {
|
||||||
|
"abbrev": "1",
|
||||||
|
"osenv": "^0.1.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"npm-bundled": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"bundled": true
|
||||||
|
},
|
||||||
|
"npm-packlist": {
|
||||||
|
"version": "1.1.12",
|
||||||
|
"bundled": true,
|
||||||
|
"requires": {
|
||||||
|
"ignore-walk": "^3.0.1",
|
||||||
|
"npm-bundled": "^1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"npmlog": {
|
||||||
|
"version": "4.1.2",
|
||||||
|
"bundled": true,
|
||||||
|
"requires": {
|
||||||
|
"are-we-there-yet": "~1.1.2",
|
||||||
|
"console-control-strings": "~1.1.0",
|
||||||
|
"gauge": "~2.7.3",
|
||||||
|
"set-blocking": "~2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"number-is-nan": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"bundled": true
|
||||||
|
},
|
||||||
|
"object-assign": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"bundled": true
|
||||||
|
},
|
||||||
|
"once": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"bundled": true,
|
||||||
|
"requires": {
|
||||||
|
"wrappy": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"os-homedir": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"bundled": true
|
||||||
|
},
|
||||||
|
"os-tmpdir": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"bundled": true
|
||||||
|
},
|
||||||
|
"osenv": {
|
||||||
|
"version": "0.1.5",
|
||||||
|
"bundled": true,
|
||||||
|
"requires": {
|
||||||
|
"os-homedir": "^1.0.0",
|
||||||
|
"os-tmpdir": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"path-is-absolute": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"bundled": true
|
||||||
|
},
|
||||||
|
"process-nextick-args": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"bundled": true
|
||||||
|
},
|
||||||
|
"rc": {
|
||||||
|
"version": "1.2.8",
|
||||||
|
"bundled": true,
|
||||||
|
"requires": {
|
||||||
|
"deep-extend": "^0.6.0",
|
||||||
|
"ini": "~1.3.0",
|
||||||
|
"minimist": "^1.2.0",
|
||||||
|
"strip-json-comments": "~2.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"minimist": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"bundled": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"readable-stream": {
|
||||||
|
"version": "2.3.5",
|
||||||
|
"bundled": true,
|
||||||
|
"requires": {
|
||||||
|
"core-util-is": "~1.0.0",
|
||||||
|
"inherits": "~2.0.3",
|
||||||
|
"isarray": "~1.0.0",
|
||||||
|
"process-nextick-args": "~2.0.0",
|
||||||
|
"safe-buffer": "~5.1.1",
|
||||||
|
"string_decoder": "~1.0.3",
|
||||||
|
"util-deprecate": "~1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rimraf": {
|
||||||
|
"version": "2.6.2",
|
||||||
|
"bundled": true,
|
||||||
|
"requires": {
|
||||||
|
"glob": "^7.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"safe-buffer": {
|
||||||
|
"version": "5.1.1",
|
||||||
|
"bundled": true
|
||||||
|
},
|
||||||
|
"safer-buffer": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"bundled": true
|
||||||
|
},
|
||||||
|
"sax": {
|
||||||
|
"version": "1.2.4",
|
||||||
|
"bundled": true
|
||||||
|
},
|
||||||
|
"semver": {
|
||||||
|
"version": "5.6.0",
|
||||||
|
"bundled": true
|
||||||
|
},
|
||||||
|
"set-blocking": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"bundled": true
|
||||||
|
},
|
||||||
|
"signal-exit": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"bundled": true
|
||||||
|
},
|
||||||
|
"string-width": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"bundled": true,
|
||||||
|
"requires": {
|
||||||
|
"code-point-at": "^1.0.0",
|
||||||
|
"is-fullwidth-code-point": "^1.0.0",
|
||||||
|
"strip-ansi": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"string_decoder": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"bundled": true,
|
||||||
|
"requires": {
|
||||||
|
"safe-buffer": "~5.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"strip-ansi": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"bundled": true,
|
||||||
|
"requires": {
|
||||||
|
"ansi-regex": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"strip-json-comments": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"bundled": true
|
||||||
|
},
|
||||||
|
"tar": {
|
||||||
|
"version": "4.4.8",
|
||||||
|
"bundled": true,
|
||||||
|
"requires": {
|
||||||
|
"chownr": "^1.1.1",
|
||||||
|
"fs-minipass": "^1.2.5",
|
||||||
|
"minipass": "^2.3.4",
|
||||||
|
"minizlib": "^1.1.1",
|
||||||
|
"mkdirp": "^0.5.0",
|
||||||
|
"safe-buffer": "^5.1.2",
|
||||||
|
"yallist": "^3.0.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"safe-buffer": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"bundled": true
|
||||||
|
},
|
||||||
|
"yallist": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"bundled": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"util-deprecate": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"bundled": true
|
||||||
|
},
|
||||||
|
"wide-align": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"bundled": true,
|
||||||
|
"requires": {
|
||||||
|
"string-width": "^1.0.2 || 2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"wrappy": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"bundled": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"bcrypt-pbkdf": {
|
"bcrypt-pbkdf": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
|
||||||
|
@ -2480,7 +2910,7 @@
|
||||||
"cache-base": {
|
"cache-base": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
|
||||||
"integrity": "sha1-Cn9GQWgxyLZi7jb+TnxZ129marI=",
|
"integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"collection-visit": "^1.0.0",
|
"collection-visit": "^1.0.0",
|
||||||
|
@ -2727,7 +3157,7 @@
|
||||||
"class-utils": {
|
"class-utils": {
|
||||||
"version": "0.3.6",
|
"version": "0.3.6",
|
||||||
"resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
|
"resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
|
||||||
"integrity": "sha1-+TNprouafOAv1B+q0MqDAzGQxGM=",
|
"integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"arr-union": "^3.1.0",
|
"arr-union": "^3.1.0",
|
||||||
|
@ -2887,7 +3317,7 @@
|
||||||
},
|
},
|
||||||
"string_decoder": {
|
"string_decoder": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
"resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
@ -4019,7 +4449,7 @@
|
||||||
"dot-prop": {
|
"dot-prop": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz",
|
||||||
"integrity": "sha1-HxngwuGqDjJ5fEl5nyg3rGr2nFc=",
|
"integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"is-obj": "^1.0.0"
|
"is-obj": "^1.0.0"
|
||||||
|
@ -4179,7 +4609,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fs-extra": {
|
"fs-extra": {
|
||||||
"version": "0.30.0",
|
"version": "0.30.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz",
|
"resolved": "http://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz",
|
||||||
"integrity": "sha1-8jP/zAjU2n1DLapEl3aYnbHfk/A=",
|
"integrity": "sha1-8jP/zAjU2n1DLapEl3aYnbHfk/A=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
@ -4192,7 +4622,7 @@
|
||||||
},
|
},
|
||||||
"jsonfile": {
|
"jsonfile": {
|
||||||
"version": "2.4.0",
|
"version": "2.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
|
"resolved": "http://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
|
||||||
"integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=",
|
"integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
@ -5224,7 +5654,7 @@
|
||||||
},
|
},
|
||||||
"file-loader": {
|
"file-loader": {
|
||||||
"version": "1.1.11",
|
"version": "1.1.11",
|
||||||
"resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz",
|
"resolved": "http://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz",
|
||||||
"integrity": "sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==",
|
"integrity": "sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
@ -6224,7 +6654,7 @@
|
||||||
"global-modules": {
|
"global-modules": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz",
|
||||||
"integrity": "sha1-bXcPDrUjrHgWTXK15xqIdyZcw+o=",
|
"integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"global-prefix": "^1.0.1",
|
"global-prefix": "^1.0.1",
|
||||||
|
@ -6489,7 +6919,7 @@
|
||||||
},
|
},
|
||||||
"kind-of": {
|
"kind-of": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz",
|
"resolved": "http://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz",
|
||||||
"integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=",
|
"integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
@ -6654,7 +7084,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"es6-promise": {
|
"es6-promise": {
|
||||||
"version": "3.3.1",
|
"version": "3.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz",
|
"resolved": "http://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz",
|
||||||
"integrity": "sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=",
|
"integrity": "sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
@ -7436,7 +7866,7 @@
|
||||||
"is-absolute": {
|
"is-absolute": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz",
|
||||||
"integrity": "sha1-OV4a6EsR8mrReV5zwXN45IowFXY=",
|
"integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"is-relative": "^1.0.0",
|
"is-relative": "^1.0.0",
|
||||||
|
@ -7675,7 +8105,7 @@
|
||||||
"is-plain-object": {
|
"is-plain-object": {
|
||||||
"version": "2.0.4",
|
"version": "2.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
|
||||||
"integrity": "sha1-LBY7P6+xtgbZ0Xko8FwqHDjgdnc=",
|
"integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"isobject": "^3.0.1"
|
"isobject": "^3.0.1"
|
||||||
|
@ -7711,7 +8141,7 @@
|
||||||
"is-relative": {
|
"is-relative": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz",
|
||||||
"integrity": "sha1-obtpNc6MXboei5dUubLcwCDiJg0=",
|
"integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"is-unc-path": "^1.0.0"
|
"is-unc-path": "^1.0.0"
|
||||||
|
@ -7754,7 +8184,7 @@
|
||||||
"is-unc-path": {
|
"is-unc-path": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz",
|
||||||
"integrity": "sha1-1zHoiY7QkKEsNSrS6u1Qla0yLJ0=",
|
"integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"unc-path-regex": "^0.1.2"
|
"unc-path-regex": "^0.1.2"
|
||||||
|
@ -7855,14 +8285,14 @@
|
||||||
},
|
},
|
||||||
"jasmine-core": {
|
"jasmine-core": {
|
||||||
"version": "2.99.1",
|
"version": "2.99.1",
|
||||||
"resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.99.1.tgz",
|
"resolved": "http://registry.npmjs.org/jasmine-core/-/jasmine-core-2.99.1.tgz",
|
||||||
"integrity": "sha1-5kAN8ea1bhMLYcS80JPap/boyhU=",
|
"integrity": "sha1-5kAN8ea1bhMLYcS80JPap/boyhU=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"jasmine-spec-reporter": {
|
"jasmine-spec-reporter": {
|
||||||
"version": "4.2.1",
|
"version": "4.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-4.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-4.2.1.tgz",
|
||||||
"integrity": "sha1-HWMq7ANBZwrTJPkrqEtLMrNeniI=",
|
"integrity": "sha512-FZBoZu7VE5nR7Nilzy+Np8KuVIOxF4oXDPDknehCYBDE080EnlPu0afdZNmpGDBRCUBv3mj5qgqCRmk6W/K8vg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"colors": "1.1.2"
|
"colors": "1.1.2"
|
||||||
|
@ -8150,7 +8580,7 @@
|
||||||
"karma-chrome-launcher": {
|
"karma-chrome-launcher": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-2.2.0.tgz",
|
||||||
"integrity": "sha1-zxudBxNswY/iOTJ9JGVMPbw2is8=",
|
"integrity": "sha512-uf/ZVpAabDBPvdPdveyk1EPgbnloPvFFGgmRhYLTDH7gEB4nZdSBk8yTU47w1g/drLSx5uMOkjKk7IWKfWg/+w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"fs-access": "^1.0.0",
|
"fs-access": "^1.0.0",
|
||||||
|
@ -9982,7 +10412,7 @@
|
||||||
},
|
},
|
||||||
"minimist": {
|
"minimist": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
"resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
|
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
|
||||||
},
|
},
|
||||||
"minstache": {
|
"minstache": {
|
||||||
|
@ -10364,7 +10794,7 @@
|
||||||
},
|
},
|
||||||
"named-placeholders": {
|
"named-placeholders": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.1.tgz",
|
"resolved": "http://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.1.tgz",
|
||||||
"integrity": "sha1-O3oNJiA910s6nfTJz7gnsvuQfmQ=",
|
"integrity": "sha1-O3oNJiA910s6nfTJz7gnsvuQfmQ=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
@ -10373,7 +10803,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lru-cache": {
|
"lru-cache": {
|
||||||
"version": "2.5.0",
|
"version": "2.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz",
|
"resolved": "http://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz",
|
||||||
"integrity": "sha1-2COIrpyWC+y+oMc7uet5tsbOmus=",
|
"integrity": "sha1-2COIrpyWC+y+oMc7uet5tsbOmus=",
|
||||||
"dev": true
|
"dev": true
|
||||||
}
|
}
|
||||||
|
@ -10382,8 +10812,7 @@
|
||||||
"nan": {
|
"nan": {
|
||||||
"version": "2.12.1",
|
"version": "2.12.1",
|
||||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz",
|
"resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz",
|
||||||
"integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==",
|
"integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"nanoid": {
|
"nanoid": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
|
@ -11354,7 +11783,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"minimist": {
|
"minimist": {
|
||||||
"version": "0.0.10",
|
"version": "0.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
|
"resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
|
||||||
"integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=",
|
"integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
@ -13405,7 +13834,7 @@
|
||||||
},
|
},
|
||||||
"safe-regex": {
|
"safe-regex": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
|
"resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
|
||||||
"integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
|
"integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
@ -13529,7 +13958,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"source-map": {
|
"source-map": {
|
||||||
"version": "0.4.4",
|
"version": "0.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
|
"resolved": "http://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
|
||||||
"integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
|
"integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
@ -13669,7 +14098,7 @@
|
||||||
"set-value": {
|
"set-value": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz",
|
||||||
"integrity": "sha1-ca5KiPD+77v1LR6mBPP7MV67YnQ=",
|
"integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"extend-shallow": "^2.0.1",
|
"extend-shallow": "^2.0.1",
|
||||||
|
@ -13856,7 +14285,7 @@
|
||||||
"snapdragon-node": {
|
"snapdragon-node": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
|
||||||
"integrity": "sha1-bBdfhv8UvbByRWPo88GwIaKGhTs=",
|
"integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"define-property": "^1.0.0",
|
"define-property": "^1.0.0",
|
||||||
|
@ -13907,7 +14336,7 @@
|
||||||
"snapdragon-util": {
|
"snapdragon-util": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
|
||||||
"integrity": "sha1-+VZHlIbyrNeXAGk/b3uAXkWrVuI=",
|
"integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"kind-of": "^3.2.0"
|
"kind-of": "^3.2.0"
|
||||||
|
@ -14252,7 +14681,7 @@
|
||||||
"split-string": {
|
"split-string": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
|
||||||
"integrity": "sha1-fLCd2jqGWFcFxks5pkZgOGguj+I=",
|
"integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"extend-shallow": "^3.0.0"
|
"extend-shallow": "^3.0.0"
|
||||||
|
@ -14261,7 +14690,7 @@
|
||||||
"split2": {
|
"split2": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/split2/-/split2-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/split2/-/split2-2.2.0.tgz",
|
||||||
"integrity": "sha1-GGsldbz4PoW30YRldWI47k7kJJM=",
|
"integrity": "sha512-RAb22TG39LhI31MbreBgIuKiIKhVsawfTgEGqKHTK87aG+ul/PB8Sqoi3I7kVdRWiCfrKxK3uo4/YUkpNvhPbw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"through2": "^2.0.2"
|
"through2": "^2.0.2"
|
||||||
|
@ -14442,7 +14871,7 @@
|
||||||
},
|
},
|
||||||
"string_decoder": {
|
"string_decoder": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
"resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
@ -14794,7 +15223,7 @@
|
||||||
},
|
},
|
||||||
"style-loader": {
|
"style-loader": {
|
||||||
"version": "0.20.3",
|
"version": "0.20.3",
|
||||||
"resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.20.3.tgz",
|
"resolved": "http://registry.npmjs.org/style-loader/-/style-loader-0.20.3.tgz",
|
||||||
"integrity": "sha512-2I7AVP73MvK33U7B9TKlYZAqdROyMXDYSMvHLX43qy3GCOaJNiV6i0v/sv9idWIaQ42Yn2dNv79Q5mKXbKhAZg==",
|
"integrity": "sha512-2I7AVP73MvK33U7B9TKlYZAqdROyMXDYSMvHLX43qy3GCOaJNiV6i0v/sv9idWIaQ42Yn2dNv79Q5mKXbKhAZg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
@ -15293,7 +15722,7 @@
|
||||||
"touch": {
|
"touch": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz",
|
||||||
"integrity": "sha1-/jZfX3XsntTlaCXgu3bSSrdK+Ds=",
|
"integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"nopt": "~1.0.10"
|
"nopt": "~1.0.10"
|
||||||
|
@ -15792,7 +16221,7 @@
|
||||||
"useragent": {
|
"useragent": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/useragent/-/useragent-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/useragent/-/useragent-2.3.0.tgz",
|
||||||
"integrity": "sha1-IX+UOtVAyyEoZYqyP8lg9qiMmXI=",
|
"integrity": "sha512-4AoH4pxuSvHCjqLO04sU6U/uE65BYza8l/KKBS0b0hnUPWi+cQ2BpeTEwejCSx9SPV5/U03nniDTrWx5NrmKdw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"lru-cache": "4.1.x",
|
"lru-cache": "4.1.x",
|
||||||
|
@ -16514,7 +16943,7 @@
|
||||||
},
|
},
|
||||||
"globby": {
|
"globby": {
|
||||||
"version": "6.1.0",
|
"version": "6.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
|
"resolved": "http://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
|
||||||
"integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=",
|
"integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
@ -16527,7 +16956,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"pify": {
|
"pify": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
|
"resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
|
||||||
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
|
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
|
||||||
"dev": true
|
"dev": true
|
||||||
}
|
}
|
||||||
|
@ -16915,7 +17344,7 @@
|
||||||
"write-file-atomic": {
|
"write-file-atomic": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.3.0.tgz",
|
||||||
"integrity": "sha1-H/YVdcLipOjlENb6TiQ8zhg5mas=",
|
"integrity": "sha512-xuPeK4OdjWqtfi59ylvVL0Yn35SF3zgcAcv7rBPFHVaEapaDr4GdGgm3j7ckTwH9wHL7fGmgfAnb0+THrHb8tA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"graceful-fs": "^4.1.11",
|
"graceful-fs": "^4.1.11",
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
"description": "Salix application",
|
"description": "Salix application",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"bcrypt": "^3.0.3",
|
||||||
"compression": "^1.7.3",
|
"compression": "^1.7.3",
|
||||||
"cookie-parser": "^1.4.3",
|
"cookie-parser": "^1.4.3",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
|
|
|
@ -81,11 +81,6 @@ let baseConfig = {
|
||||||
template: 'front/salix/index.ejs',
|
template: 'front/salix/index.ejs',
|
||||||
filename: 'index.html',
|
filename: 'index.html',
|
||||||
chunks: ['salix']
|
chunks: ['salix']
|
||||||
}),
|
|
||||||
new HtmlWebpackPlugin({
|
|
||||||
template: 'front/auth/auth.ejs',
|
|
||||||
filename: 'auth.html',
|
|
||||||
chunks: ['auth']
|
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
devtool: 'source-map',
|
devtool: 'source-map',
|
||||||
|
|
|
@ -2,6 +2,5 @@ buildDir: dist
|
||||||
devServerPort: 3500
|
devServerPort: 3500
|
||||||
publicPath: '/static'
|
publicPath: '/static'
|
||||||
entry: {
|
entry: {
|
||||||
salix: salix,
|
salix: salix
|
||||||
auth: auth
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue