diff --git a/client/core/src/core.js b/client/core/src/core.js
index 6bc5b8bdc..2889a690e 100644
--- a/client/core/src/core.js
+++ b/client/core/src/core.js
@@ -1,5 +1,6 @@
import './styles/mdl-override.css';
import './styles/mdi-override.css';
+import './styles/zoom-image.css';
export * from './module';
export * from './directives/index';
diff --git a/client/core/src/directives/index.js b/client/core/src/directives/index.js
index da672b330..9a6531b1e 100644
--- a/client/core/src/directives/index.js
+++ b/client/core/src/directives/index.js
@@ -4,3 +4,4 @@ import './dialog';
import './validation';
import './acl';
import './on-error-src';
+import './zoom-image';
diff --git a/client/core/src/directives/specs/zoom-image.spec.js b/client/core/src/directives/specs/zoom-image.spec.js
new file mode 100644
index 000000000..1ee7bbd79
--- /dev/null
+++ b/client/core/src/directives/specs/zoom-image.spec.js
@@ -0,0 +1,71 @@
+describe('Directive zoomImage', () => {
+ let idContainer = 'zoomImage';
+ let compile;
+ let scope;
+ let srcDefault = 'http://default.img.jpg/';
+ let srcZoom = 'http://zoom.img.jpg/';
+ let findContainer;
+
+ beforeEach(() => {
+ angular.mock.module('client');
+ });
+
+ beforeEach(angular.mock.inject(($compile, $rootScope) => {
+ compile = $compile;
+ scope = $rootScope.$new();
+ }));
+
+ afterEach(() => {
+ findContainer = document.getElementById(idContainer);
+ if (findContainer) {
+ findContainer.parentNode.removeChild(findContainer);
+ findContainer = undefined;
+ }
+ });
+
+ function getCompiledImage(imgHtml) {
+ let element = angular.element(imgHtml);
+ var compiledElement = compile(element)(scope);
+ scope.$digest();
+ return compiledElement;
+ }
+
+ it('should create zoom container when click into image', () => {
+ let image = getCompiledImage(``);
+ image[0].click();
+ findContainer = document.getElementById(idContainer);
+
+ expect(findContainer).not.toBeNull();
+ });
+
+ it('should detroy zoom container when click outside zoomed image', () => {
+ let image = getCompiledImage(``);
+ image[0].click();
+ findContainer = document.getElementById(idContainer);
+
+ let findOutsideImage = findContainer.querySelector('.zoomImage-background');
+ findOutsideImage.click();
+
+ findContainer = document.getElementById(idContainer);
+
+ expect(findContainer).toBeNull();
+ });
+
+ it('should create new image, into zoom container, with src as original image src', () => {
+ let image = getCompiledImage(``);
+ image[0].click();
+ findContainer = document.getElementById(idContainer);
+ let findNewImage = findContainer.querySelector('img');
+
+ expect(findNewImage.src).toEqual(srcDefault);
+ });
+
+ it('should create new image, into zoom container, with src likes zoomImage value', () => {
+ let image = getCompiledImage(``);
+ image[0].click();
+ findContainer = document.getElementById(idContainer);
+ let findNewImage = findContainer.querySelector('img');
+
+ expect(findNewImage.src).toEqual(srcZoom);
+ });
+});
diff --git a/client/core/src/directives/zoom-image.js b/client/core/src/directives/zoom-image.js
new file mode 100644
index 000000000..dc75eda40
--- /dev/null
+++ b/client/core/src/directives/zoom-image.js
@@ -0,0 +1,89 @@
+import ngModule from '../module';
+
+export function directive($timeout) {
+ let idContainer = 'zoomImage';
+ let container;
+ let background;
+ let image;
+
+ function createContainers(src) {
+ if (document.getElementById(idContainer)) {
+ destroyContainers();
+ }
+ container = document.createElement('div');
+ container.id = idContainer;
+
+ background = document.createElement('div');
+ background.className = 'zoomImage-background';
+ container.appendChild(background);
+
+ image = document.createElement('img');
+ image.src = src;
+ container.appendChild(image);
+
+ document.body.appendChild(container);
+
+ $timeout(() => {
+ resizeImage();
+ container.className = 'open';
+ }, 250);
+ }
+
+ function addListeners() {
+ background.addEventListener('click', destroyContainers);
+ document.addEventListener('keydown', e => keyDownHandler(e));
+ window.addEventListener('resize', resizeImage);
+ }
+
+ function removeListeners() {
+ if (container) {
+ background.removeEventListener('click', destroyContainers);
+ document.removeEventListener('keydown', e => keyDownHandler(e));
+ window.removeEventListener('resize', resizeImage);
+ }
+ }
+
+ function keyDownHandler(event) {
+ if (event.keyCode === 27) {
+ destroyContainers();
+ }
+ }
+
+ function destroyContainers() {
+ if (document.getElementById(idContainer)) {
+ removeListeners();
+ container.parentNode.removeChild(container);
+ }
+
+ container = undefined;
+ background = undefined;
+ image = undefined;
+ }
+
+ function resizeImage() {
+ if (image) {
+ image.style.marginLeft = `-${Math.floor(image.clientWidth / 2)}px`;
+ image.style.marginTop = `-${Math.floor(image.clientHeight / 2)}px`;
+ }
+ }
+
+ return {
+ restrict: 'A',
+ priority: 9999,
+ link: function($scope, $element, $attrs) {
+ $element.on('click', function(event) {
+ let src = $attrs.zoomImage || $attrs.src;
+ if (src) {
+ createContainers(src);
+ addListeners();
+ } else
+ throw new Error('No image source detected');
+
+ event.preventDefault();
+ });
+ }
+ };
+}
+directive.$inject = ['$timeout'];
+
+ngModule.directive('zoomImage', directive);
diff --git a/client/core/src/styles/zoom-image.css b/client/core/src/styles/zoom-image.css
new file mode 100644
index 000000000..f21db8d13
--- /dev/null
+++ b/client/core/src/styles/zoom-image.css
@@ -0,0 +1,40 @@
+img[zoom-image]{
+ cursor: zoom-in;
+}
+
+div#zoomImage, div#zoomImage .zoomImage-background {
+ width: 100%;
+ height: 100%;
+ position: fixed;
+ top: 0;
+ z-index: 11;
+}
+div#zoomImage{
+ opacity: 0;
+ transition: visibility 0s, opacity 0.5s linear;
+}
+
+div#zoomImage .zoomImage-background{
+ background: rgba(0, 0, 0, 0.7);
+ cursor: zoom-out;
+}
+div#zoomImage img{
+ z-index: 12;
+ position: fixed;
+ max-height: 98%;
+ max-width: 98%;
+ left: 50%;
+ top: 50%;
+ opacity: 0;
+ transition: visibility 0s, opacity 1s linear;
+}
+
+div#zoomImage.open {
+ visibility: visible;
+ opacity: 1;
+}
+
+div#zoomImage.open img{
+ visibility: visible;
+ opacity: 1;
+}
\ No newline at end of file
diff --git a/client/item/src/card/item-card.js b/client/item/src/card/item-card.js
index aeb921651..903631d42 100644
--- a/client/item/src/card/item-card.js
+++ b/client/item/src/card/item-card.js
@@ -16,7 +16,6 @@ class ItemCard {
{relation: "producer"},
{relation: "intrastat"},
{relation: "expence"},
- {relation: "taxClass"},
{relation: "itemTag", scope: {order: "priority ASC", include: {relation: "tag"}}}
]
};
diff --git a/client/item/src/card/item-card.spec.js b/client/item/src/card/item-card.spec.js
index 5447d007e..72e09a306 100644
--- a/client/item/src/card/item-card.spec.js
+++ b/client/item/src/card/item-card.spec.js
@@ -21,8 +21,8 @@ describe('Item', () => {
describe('$onInit()', () => {
it('should request to patch the propagation of tax status', () => {
controller.client = {id: 123, isEqualizated: false};
- $httpBackend.whenGET('/item/api/Items/undefined?filter={"include":[{"relation":"itemType"},{"relation":"origin"},{"relation":"ink"},{"relation":"producer"},{"relation":"intrastat"},{"relation":"expence"},{"relation":"taxClass"},{"relation":"itemTag","scope":{"order":"priority ASC","include":{"relation":"tag"}}}]}').respond({data: 'item'});
- $httpBackend.expectGET('/item/api/Items/undefined?filter={"include":[{"relation":"itemType"},{"relation":"origin"},{"relation":"ink"},{"relation":"producer"},{"relation":"intrastat"},{"relation":"expence"},{"relation":"taxClass"},{"relation":"itemTag","scope":{"order":"priority ASC","include":{"relation":"tag"}}}]}');
+ $httpBackend.whenGET('/item/api/Items/undefined?filter={"include":[{"relation":"itemType"},{"relation":"origin"},{"relation":"ink"},{"relation":"producer"},{"relation":"intrastat"},{"relation":"expence"},{"relation":"itemTag","scope":{"order":"priority ASC","include":{"relation":"tag"}}}]}').respond({data: 'item'});
+ $httpBackend.expectGET('/item/api/Items/undefined?filter={"include":[{"relation":"itemType"},{"relation":"origin"},{"relation":"ink"},{"relation":"producer"},{"relation":"intrastat"},{"relation":"expence"},{"relation":"itemTag","scope":{"order":"priority ASC","include":{"relation":"tag"}}}]}');
controller.$onInit();
$httpBackend.flush();
diff --git a/client/item/src/create/item-create.html b/client/item/src/create/item-create.html
index d57806715..72fd7e15a 100644
--- a/client/item/src/create/item-create.html
+++ b/client/item/src/create/item-create.html
@@ -12,6 +12,8 @@
New item
+
+
-
-
{{$parent.$parent.item.description}}
-
-
- {{$parent.$parent.item.description}}
-
-
diff --git a/client/item/src/descriptor/item-descriptor.html b/client/item/src/descriptor/item-descriptor.html
index 286e0c733..3837e838a 100644
--- a/client/item/src/descriptor/item-descriptor.html
+++ b/client/item/src/descriptor/item-descriptor.html
@@ -4,7 +4,9 @@
-
+
Id: {{$ctrl.item.id}}
diff --git a/client/item/src/filter-panel/filter-panel.html b/client/item/src/filter-panel/filter-panel.html
index 607e7e7cc..2f09c471c 100644
--- a/client/item/src/filter-panel/filter-panel.html
+++ b/client/item/src/filter-panel/filter-panel.html
@@ -4,53 +4,42 @@
-
-
+ field="$ctrl.filter.typeFk">
-
+ field="$ctrl.filter.inkFk">
-
+ field="$ctrl.filter.originFk">
-
+ field="$ctrl.filter.producerFk">
-
-
diff --git a/client/modules.yml b/client/modules.yml
index 0236ce1e3..95ce80525 100644
--- a/client/modules.yml
+++ b/client/modules.yml
@@ -1,8 +1,8 @@
-salix: []
auth: []
-core: []
client: []
-production: []
-route: []
-locator: []
+core: []
item: []
+locator: []
+production: []
+salix: []
+route: []
diff --git a/gulpfile.js b/gulpfile.js
index b69e22734..4a4c29ec2 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -9,28 +9,30 @@ const log = require('fancy-log');
// Configuration
-const isWindows = /^win/.test(process.platform);
+let isWindows = /^win/.test(process.platform);
if (argv.NODE_ENV)
process.env.NODE_ENV = argv.NODE_ENV;
-const env = process.env.NODE_ENV ? process.env.NODE_ENV : 'development';
+let env = process.env.NODE_ENV ? process.env.NODE_ENV : 'development';
-const langs = ['es', 'en'];
-const srcDir = './client';
-const servicesDir = './services';
-const nginxDir = `${servicesDir}/nginx`;
-const buildDir = `${nginxDir}/static`;
+let langs = ['es', 'en'];
+let srcDir = './client';
+let servicesDir = './services';
+let wpConfig = require('./webpack.config.yml');
+let buildDir = wpConfig.buildDir;
+let devServerPort = wpConfig.devServerPort;
+
+let nginxDir = `${servicesDir}/nginx`;
let proxyConf = require(`${nginxDir}/config.yml`);
let proxyEnvFile = `${nginxDir}/config.${env}.yml`;
if (fs.existsSync(proxyEnvFile))
Object.assign(proxyConf, require(proxyEnvFile));
-const defaultService = proxyConf.main;
-const defaultPort = proxyConf.defaultPort;
-const devServerPort = proxyConf.devServerPort;
+let defaultService = proxyConf.main;
+let defaultPort = proxyConf.defaultPort;
// Development
@@ -42,30 +44,51 @@ gulp.task('client', ['build-clean'], () => {
return gulp.start('watch', 'routes', 'locales', 'webpack-dev-server');
});
+/**
+ * Starts all backend services, including the nginx proxy and the database.
+ */
gulp.task('services', async () => {
await runSequenceP('docker-start', 'services-only', 'nginx');
});
+/**
+ * Starts all backend services.
+ */
gulp.task('services-only', async () => {
const services = await getServices();
for (let service of services)
require(service.index).start(service.port);
});
+/**
+ * Runs the e2e tests, restoring the fixtures first.
+ */
gulp.task('e2e', ['docker-rebuild'], async () => {
await runSequenceP('e2e-only');
});
+/**
+ * Runs the e2e tests.
+ */
gulp.task('e2e-only', () => {
const jasmine = require('gulp-jasmine');
return gulp.src('./e2e_tests.js')
.pipe(jasmine({reporter: 'none'}));
});
+/**
+ * Cleans all generated project files.
+ */
gulp.task('clean', ['build-clean', 'nginx-clean']);
+/**
+ * Alias for the 'install' task.
+ */
gulp.task('i', ['install']);
+/**
+ * Installs node dependencies in all project directories.
+ */
gulp.task('install', () => {
const install = require('gulp-install');
const print = require('gulp-print');
@@ -123,6 +146,9 @@ gulp.task('docker-compose', async () => {
await fs.writeFile('./docker-compose.yml', ymlString);
});
+/**
+ * Cleans all files generated by the 'build' task.
+ */
gulp.task('build-clean', () => {
const del = require('del');
const files = [
@@ -139,10 +165,16 @@ gulp.task('build-clean', () => {
let nginxConf = 'temp/nginx.conf';
let nginxTemp = `${nginxDir}/temp`;
+/**
+ * Starts the nginx process, if it is started, restarts it.
+ */
gulp.task('nginx', async () => {
await runSequenceP('nginx-stop', 'nginx-start');
});
+/**
+ * Starts the nginx process, generating it's configuration file first.
+ */
gulp.task('nginx-start', ['nginx-conf'], async () => {
let nginxBin = await nginxGetBin();
@@ -153,6 +185,9 @@ gulp.task('nginx-start', ['nginx-conf'], async () => {
await execP(`${nginxBin} -c "${nginxConf}" -p "${nginxDir}"`);
});
+/**
+ * Stops the nginx process.
+ */
gulp.task('nginx-stop', async () => {
try {
let nginxBin = await nginxGetBin();
@@ -161,6 +196,11 @@ gulp.task('nginx-stop', async () => {
} catch (e) {}
});
+/**
+ * Generates the nginx configuration file. If NODE_ENV is defined and the
+ * 'nginx.[environment].mst' file exists, it is used as a template, otherwise,
+ * the 'nginx.mst' template file is used.
+ */
gulp.task('nginx-conf', ['nginx-stop'], async () => {
const mustache = require('mustache');
@@ -187,6 +227,9 @@ gulp.task('nginx-conf', ['nginx-stop'], async () => {
await fs.writeFile(`${nginxTemp}/nginx.conf`, nginxConf);
});
+/**
+ * Cleans all files generated by nginx.
+ */
gulp.task('nginx-clean', ['nginx-stop'], () => {
const del = require('del');
return del([`${nginxTemp}/*`], {force: true});
@@ -280,6 +323,11 @@ gulp.task('webpack-dev-server', function() {
let localeFiles = `${srcDir}/**/locale/*.yml`;
+/**
+ * Mixes all locale files into one JSON file per module and language. It looks
+ * recursively in all project directories for locale folders with per language
+ * yaml translation files.
+ */
gulp.task('locales', function() {
const extend = require('gulp-extend');
const yaml = require('gulp-yaml');
@@ -323,6 +371,10 @@ gulp.task('watch', function() {
// Docker
+/**
+ * Rebuilds the docker and it's image, if these already exist, destroys and
+ * rebuilds them.
+ */
gulp.task('docker-rebuild', async () => {
try {
await execP('docker rm -f dblocal');
@@ -334,16 +386,22 @@ gulp.task('docker-rebuild', async () => {
await runSequenceP('docker-run');
});
+/**
+ * Does the minium effort to start the docker, if it doesn't exists calls
+ * the 'docker-run' task, if it is started does nothing. Keep in mind that when
+ * you do not rebuild the docker you may be using an outdated version of it.
+ * See the 'docker-rebuild' task for more info.
+ */
gulp.task('docker-start', async () => {
- let result;
-
+ let state;
try {
- result = await execP('docker container inspect -f "{{json .State}}" dblocal');
+ let result = await execP('docker container inspect -f "{{json .State}}" dblocal');
+ state = JSON.parse(result.stdout);
} catch (err) {
return await runSequenceP('docker-run');
}
- switch (JSON.parse(result.stdout).Status) {
+ switch (state.Status) {
case 'running':
return;
case 'exited':
@@ -353,6 +411,9 @@ gulp.task('docker-start', async () => {
}
});
+/**
+ * Runs the docker, if the container or it's image doesn't exists builds them.
+ */
gulp.task('docker-run', async () => {
try {
await execP('docker image inspect -f "{{json .Id}}" dblocal');
@@ -364,29 +425,58 @@ gulp.task('docker-run', async () => {
await runSequenceP('docker-wait');
});
+/**
+ * Waits until MySQL docker is started and ready to serve connections.
+ */
gulp.task('docker-wait', callback => {
- let maxInterval = 30 * 60000;
- let interval = 1000;
- let timer = 0;
+ const mysql = require('mysql2');
+
+ let interval = 1;
+ let elapsedTime = 0;
+ let maxInterval = 30 * 60;
+
log('Waiting for MySQL init process...');
- let waitForLocaldb = setInterval(() => {
- if (timer < maxInterval) {
- timer += interval;
- exec('docker logs --tail 1 dblocal', (err, stdout, stderr) => {
- if (stderr.includes('starting as process 1') || err) {
- clearInterval(waitForLocaldb);
- callback(err);
- }
- });
- } else {
- clearInterval(waitForLocaldb);
- callback(new Error(`MySQL not initialized whithin ${maxInterval / 1000} secs`));
+ checker();
+
+ async function checker() {
+ elapsedTime += interval;
+ let state;
+
+ try {
+ let result = await execP('docker container inspect -f "{{json .State}}" dblocal');
+ state = JSON.parse(result.stdout);
+ } catch (err) {
+ return callback(new Error(err.message));
}
- }, interval);
+
+ if (state.Status === 'exited')
+ return callback(new Error('Docker exited, please see the docker logs for more info'));
+
+ let conn = mysql.createConnection({
+ host: 'localhost',
+ user: 'root'
+ });
+ conn.on('error', () => {});
+ conn.connect(err => {
+ conn.destroy();
+ if (!err) return callback();
+
+ if (elapsedTime >= maxInterval)
+ callback(new Error(`MySQL not initialized whithin ${elapsedTime} secs`));
+ else
+ setTimeout(checker, interval * 1000);
+ });
+ }
});
// Helpers
+/**
+ * Promisified version of exec().
+ *
+ * @param {String} command The exec command
+ * @return {Promise} The promise
+ */
function execP(command) {
return new Promise((resolve, reject) => {
exec(command, (err, stdout, stderr) => {
@@ -401,6 +491,12 @@ function execP(command) {
});
}
+/**
+ * Promisified version of runSequence().
+ *
+ * @param {String} args The list of gulp task names
+ * @return {Promise} The promise
+ */
function runSequenceP() {
return new Promise((resolve, reject) => {
let args = Array.prototype.slice.call(arguments);
diff --git a/package-lock.json b/package-lock.json
index 593858160..863101475 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -263,6 +263,12 @@
"integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=",
"dev": true
},
+ "ansicolors": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.2.1.tgz",
+ "integrity": "sha1-vgiVmQl7dKXJxKhKDNvNtivYeu8=",
+ "dev": true
+ },
"anymatch": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.0.tgz",
@@ -429,6 +435,38 @@
"integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=",
"dev": true
},
+ "assets-webpack-plugin": {
+ "version": "3.5.1",
+ "resolved": "https://registry.npmjs.org/assets-webpack-plugin/-/assets-webpack-plugin-3.5.1.tgz",
+ "integrity": "sha1-kxzg1m1C6I7V5/GNZVIpQ8V6OH0=",
+ "dev": true,
+ "requires": {
+ "camelcase": "1.2.1",
+ "escape-string-regexp": "1.0.5",
+ "lodash.assign": "3.2.0",
+ "lodash.merge": "3.3.2",
+ "mkdirp": "0.5.1"
+ },
+ "dependencies": {
+ "camelcase": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
+ "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=",
+ "dev": true
+ },
+ "lodash.assign": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-3.2.0.tgz",
+ "integrity": "sha1-POnwI0tLIiPilrj6CsH+6OvKZPo=",
+ "dev": true,
+ "requires": {
+ "lodash._baseassign": "3.2.0",
+ "lodash._createassigner": "3.1.1",
+ "lodash.keys": "3.1.2"
+ }
+ }
+ }
+ },
"assign-symbols": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
@@ -1335,12 +1373,6 @@
"integrity": "sha1-TK2iGTZS6zyp7I5VyQFWacmAaXg=",
"dev": true
},
- "bignumber.js": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-4.0.4.tgz",
- "integrity": "sha1-fED1q80tZiOre5loLufbgbEYiaQ=",
- "dev": true
- },
"binary-extensions": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.8.0.tgz",
@@ -1833,6 +1865,16 @@
"integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0=",
"dev": true
},
+ "cardinal": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-1.0.0.tgz",
+ "integrity": "sha1-UOIcGwqjdyn5N33vGWtanOyTLuk=",
+ "dev": true,
+ "requires": {
+ "ansicolors": "0.2.1",
+ "redeyed": "1.0.1"
+ }
+ },
"caseless": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz",
@@ -4098,6 +4140,12 @@
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
"dev": true
},
+ "denque": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/denque/-/denque-1.2.3.tgz",
+ "integrity": "sha512-BOjyD1zPf7gqgXlXBCnCsz84cbRNfqpQNvWOUiw3Onu9s7a2afW2LyHzctoie/2KELfUoZkNHTnW02C3hCU20w==",
+ "dev": true
+ },
"depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
@@ -11062,6 +11110,18 @@
"integrity": "sha1-0iyaxmAojzhD4Wun0rXQbMon13c=",
"dev": true
},
+ "lodash._arraycopy": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/lodash._arraycopy/-/lodash._arraycopy-3.0.0.tgz",
+ "integrity": "sha1-due3wfH7klRzdIeKVi7Qaj5Q9uE=",
+ "dev": true
+ },
+ "lodash._arrayeach": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/lodash._arrayeach/-/lodash._arrayeach-3.0.0.tgz",
+ "integrity": "sha1-urFWsqkNPxu9XGU0AzSeXlkz754=",
+ "dev": true
+ },
"lodash._baseassign": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz",
@@ -11078,6 +11138,12 @@
"integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=",
"dev": true
},
+ "lodash._basefor": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash._basefor/-/lodash._basefor-3.0.3.tgz",
+ "integrity": "sha1-dVC06SGO8J+tJDQ7YSAhx5tMIMI=",
+ "dev": true
+ },
"lodash._basetostring": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz",
@@ -11279,6 +11345,23 @@
"lodash._objecttypes": "2.4.1"
}
},
+ "lodash.isplainobject": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-3.2.0.tgz",
+ "integrity": "sha1-moI4rhayAEMpYM1zRlEtASP79MU=",
+ "dev": true,
+ "requires": {
+ "lodash._basefor": "3.0.3",
+ "lodash.isarguments": "3.1.0",
+ "lodash.keysin": "3.0.8"
+ }
+ },
+ "lodash.istypedarray": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/lodash.istypedarray/-/lodash.istypedarray-3.0.6.tgz",
+ "integrity": "sha1-yaR3SYYHUB2OhJTSg7h8OSgc72I=",
+ "dev": true
+ },
"lodash.keys": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz",
@@ -11290,6 +11373,35 @@
"lodash.isarray": "3.0.4"
}
},
+ "lodash.keysin": {
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/lodash.keysin/-/lodash.keysin-3.0.8.tgz",
+ "integrity": "sha1-IsRJPrvtsUJ5YqVLRFssinZ/tH8=",
+ "dev": true,
+ "requires": {
+ "lodash.isarguments": "3.1.0",
+ "lodash.isarray": "3.0.4"
+ }
+ },
+ "lodash.merge": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-3.3.2.tgz",
+ "integrity": "sha1-DZDZPtY3sYeEN7s+IWASYNev6ZQ=",
+ "dev": true,
+ "requires": {
+ "lodash._arraycopy": "3.0.0",
+ "lodash._arrayeach": "3.0.0",
+ "lodash._createassigner": "3.1.1",
+ "lodash._getnative": "3.9.1",
+ "lodash.isarguments": "3.1.0",
+ "lodash.isarray": "3.0.4",
+ "lodash.isplainobject": "3.2.0",
+ "lodash.istypedarray": "3.0.6",
+ "lodash.keys": "3.1.2",
+ "lodash.keysin": "3.0.8",
+ "lodash.toplainobject": "3.0.0"
+ }
+ },
"lodash.mergewith": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz",
@@ -11335,6 +11447,16 @@
"lodash.escape": "3.2.0"
}
},
+ "lodash.toplainobject": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/lodash.toplainobject/-/lodash.toplainobject-3.0.0.tgz",
+ "integrity": "sha1-KHkK2ULSk9eKpmOgfs9/UsoEGY0=",
+ "dev": true,
+ "requires": {
+ "lodash._basecopy": "3.0.1",
+ "lodash.keysin": "3.0.8"
+ }
+ },
"lodash.values": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash.values/-/lodash.values-2.4.1.tgz",
@@ -11399,6 +11521,12 @@
"integrity": "sha1-4PyVEztu8nbNyIh82vJKpvFW+Po=",
"dev": true
},
+ "long": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
+ "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==",
+ "dev": true
+ },
"longest": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
@@ -11809,22 +11937,46 @@
"integrity": "sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA=",
"dev": true
},
- "mysql": {
- "version": "2.15.0",
- "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.15.0.tgz",
- "integrity": "sha1-6haEEVY0Po8uR/yJhexBzdlXO1w=",
+ "mysql2": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-1.5.2.tgz",
+ "integrity": "sha512-976p3FxXdNMRRiF6Qe/FCOwaUYw3KXVJiIYu5iE5shM7ggIASgF6G/9gd9rhpBqP8V6MVa3KQJ6Ao1xBeGBljw==",
"dev": true,
"requires": {
- "bignumber.js": "4.0.4",
- "readable-stream": "2.3.3",
+ "cardinal": "1.0.0",
+ "denque": "1.2.3",
+ "generate-function": "2.0.0",
+ "iconv-lite": "0.4.19",
+ "long": "4.0.0",
+ "lru-cache": "4.1.1",
+ "named-placeholders": "1.1.1",
+ "object-assign": "4.1.1",
+ "readable-stream": "2.3.2",
"safe-buffer": "5.1.1",
+ "seq-queue": "0.0.5",
"sqlstring": "2.3.0"
},
"dependencies": {
+ "lru-cache": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz",
+ "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==",
+ "dev": true,
+ "requires": {
+ "pseudomap": "1.0.2",
+ "yallist": "2.1.2"
+ }
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+ "dev": true
+ },
"readable-stream": {
- "version": "2.3.3",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
- "integrity": "sha1-No8lEtefnUb9/HE0mueHi7weuVw=",
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.2.tgz",
+ "integrity": "sha1-WgTfBeT1f+Pw3Gj90R3FyXx+b00=",
"dev": true,
"requires": {
"core-util-is": "1.0.2",
@@ -11839,11 +11991,34 @@
"string_decoder": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
- "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=",
+ "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
"dev": true,
"requires": {
"safe-buffer": "5.1.1"
}
+ },
+ "yallist": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
+ "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
+ "dev": true
+ }
+ }
+ },
+ "named-placeholders": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.1.tgz",
+ "integrity": "sha1-O3oNJiA910s6nfTJz7gnsvuQfmQ=",
+ "dev": true,
+ "requires": {
+ "lru-cache": "2.5.0"
+ },
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz",
+ "integrity": "sha1-2COIrpyWC+y+oMc7uet5tsbOmus=",
+ "dev": true
}
}
},
@@ -13403,6 +13578,23 @@
"strip-indent": "1.0.1"
}
},
+ "redeyed": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-1.0.1.tgz",
+ "integrity": "sha1-6WwZO0DAgWsArshCaY5hGF5VSYo=",
+ "dev": true,
+ "requires": {
+ "esprima": "3.0.0"
+ },
+ "dependencies": {
+ "esprima": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.0.0.tgz",
+ "integrity": "sha1-U88kes2ncxPlUcOqLnM0LT+099k=",
+ "dev": true
+ }
+ }
+ },
"regenerate": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.3.tgz",
@@ -13956,6 +14148,12 @@
}
}
},
+ "seq-queue": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz",
+ "integrity": "sha1-1WgS4cAXpuTnw+Ojeh2m143TyT4=",
+ "dev": true
+ },
"sequencify": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/sequencify/-/sequencify-0.0.7.tgz",
@@ -16873,6 +17071,23 @@
}
}
},
+ "webpack-merge": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.1.1.tgz",
+ "integrity": "sha512-geQsZ86YkXOVOjvPC5yv3JSNnL6/X3Kzh935AQ/gJNEYXEfJDQFu/sdFuktS9OW2JcH/SJec8TGfRdrpHshH7A==",
+ "dev": true,
+ "requires": {
+ "lodash": "4.17.5"
+ },
+ "dependencies": {
+ "lodash": {
+ "version": "4.17.5",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz",
+ "integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw==",
+ "dev": true
+ }
+ }
+ },
"webpack-sources": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.1.0.tgz",
diff --git a/package.json b/package.json
index b7a6f5b75..1cac64134 100644
--- a/package.json
+++ b/package.json
@@ -25,6 +25,7 @@
},
"devDependencies": {
"angular-mocks": "^1.6.6",
+ "assets-webpack-plugin": "^3.5.1",
"babel": "^6.23.0",
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
@@ -63,7 +64,7 @@
"merge-stream": "^1.0.1",
"minimist": "^1.2.0",
"mustache": "^2.3.0",
- "mysql": "^2.15.0",
+ "mysql2": "^1.5.2",
"nightmare": "^2.10.0",
"node-sass": "^4.7.2",
"nodemon": "^1.12.1",
@@ -74,6 +75,7 @@
"style-loader": "^0.20.1",
"webpack": "^3.10.0",
"webpack-dev-server": "^2.11.1",
+ "webpack-merge": "^4.1.1",
"yaml-loader": "^0.5.0"
},
"scripts": {
diff --git a/services/auth/Dockerfile b/services/auth/Dockerfile
index 80cb8e238..935e0809f 100644
--- a/services/auth/Dockerfile
+++ b/services/auth/Dockerfile
@@ -2,6 +2,7 @@ FROM node:8.9.4
COPY auth /app
COPY loopback /loopback
+COPY nginx/static/webpack-assets.json /loopback/server/
WORKDIR /app
diff --git a/services/auth/client/index.ejs b/services/auth/client/index.ejs
index 3e3cd7205..83e71146f 100644
--- a/services/auth/client/index.ejs
+++ b/services/auth/client/index.ejs
@@ -6,14 +6,8 @@
-
-
-
+ <% for (let jsFile of assets('auth', ['vendor'])) { %>
+
+ <% } %>