automatic project update

This commit is contained in:
Julien Dubois 2016-03-23 16:27:37 +01:00
parent 231b2d9297
commit ed41ad9816
1822 changed files with 66055 additions and 41755 deletions

6
.eslintignore Normal file
View File

@ -0,0 +1,6 @@
# more info here - http://eslint.org/docs/user-guide/configuring.html#ignoring-files-and-directories
# node_modules ignored by default
# ignore bower_components
src/main/webapp/bower_components

29
.eslintrc.json Normal file
View File

@ -0,0 +1,29 @@
// See: http://eslint.org/
// See: https://www.npmjs.com/package/eslint-plugin-angular
{
"extends": [
"eslint:recommended",
"angular"
],
"env": {
"node": true,
"browser": true
},
// severity for a rule should be one of the following: 0 = off, 1 = warning, 2 = error
"rules": {
// coding style
"wrap-iife": [2, "inside"],
"eqeqeq": 2,
"no-use-before-define": [2, "nofunc"],
"no-unused-vars": [2, {"vars": "local", "args": "none"}],
"no-multi-str": 2,
"no-irregular-whitespace": 2,
"semi": [2, "always"],
"indent": 2,
// os/git options
// we want to run on all OSes
"linebreak-style": 0,
"eol-last": 2
}
}

7
.gitignore vendored
View File

@ -1,7 +1,7 @@
######################
# Project Specific
######################
/src/main/webapp/dist
/target/www/**
######################
# Node
@ -126,3 +126,8 @@ Desktop.ini
# Maven Wrapper
######################
!.mvn/wrapper/maven-wrapper.jar
######################
# ESLint
######################
.eslintcache

View File

@ -1 +1 @@
distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.3/apache-maven-3.3.3-bin.zip
distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.9/apache-maven-3.3.9-bin.zip

View File

@ -2,9 +2,9 @@ language: node_js
node_js:
- "0.12"
sudo: false
before_install: npm install -g grunt-cli
before_install: npm install -g gulp
install: npm install
before_script: grunt build
before_script: gulp build
notifications:
webhooks:
on_success: change # options: [always|never|change] default: always

View File

@ -12,15 +12,20 @@
"prodDatabaseType": "mysql",
"useCompass": false,
"buildTool": "maven",
"frontendBuilder": "grunt",
"javaVersion": "8",
"rememberMeKey": "5c37379956bd1242f5636c8cb322c2966ad81277",
"searchEngine": "no",
"enableTranslation": true,
"useSass": false,
"applicationType": "monolith",
"testFrameworks": [
"gatling"
"gatling",
"protractor"
],
"enableSocialSignIn": false
"languages": [
"en"
],
"serverPort": 8080,
"jhipsterVersion": "3.0.0",
"enableSocialSignIn": false,
"useSass": false
}
}

View File

@ -1,362 +0,0 @@
// Generated on 2016-03-07 using generator-jhipster 2.27.2
'use strict';
var fs = require('fs');
var parseString = require('xml2js').parseString;
// Returns the second occurence of the version number
var parseVersionFromPomXml = function() {
var version;
var pomXml = fs.readFileSync('pom.xml', "utf8");
parseString(pomXml, function (err, result){
if (result.project.version && result.project.version[0]) {
version = result.project.version[0];
} else if (result.project.parent && result.project.parent[0] && result.project.parent[0].version && result.project.parent[0].version[0]) {
version = result.project.parent[0].version[0]
} else {
throw new Error('pom.xml is malformed. No version is defined');
}
});
return version;
};
// usemin custom step
var useminAutoprefixer = {
name: 'autoprefixer',
createConfig: function(context, block) {
if(block.src.length === 0) {
return {};
} else {
return require('grunt-usemin/lib/config/cssmin').createConfig(context, block) // Reuse cssmins createConfig
}
}
};
module.exports = function (grunt) {
require('load-grunt-tasks')(grunt);
require('time-grunt')(grunt);
grunt.initConfig({
yeoman: {
// configurable paths
app: require('./bower.json').appPath || 'app',
dist: 'src/main/webapp/dist'
},
watch: {
bower: {
files: ['bower.json'],
tasks: ['wiredep']
},
ngconstant: {
files: ['Gruntfile.js', 'pom.xml'],
tasks: ['ngconstant:dev']
}
},
autoprefixer: {
// src and dest is configured in a subtask called "generated" by usemin
},
wiredep: {
app: {
src: ['src/main/webapp/index.html'],
exclude: [
/angular-i18n/ // localizations are loaded dynamically
]
},
test: {
src: 'src/test/javascript/karma.conf.js',
exclude: [/angular-i18n/, /angular-scenario/],
ignorePath: /\.\.\/\.\.\//, // remove ../../ from paths of injected javascripts
devDependencies: true,
fileTypes: {
js: {
block: /(([\s\t]*)\/\/\s*bower:*(\S*))(\n|\r|.)*?(\/\/\s*endbower)/gi,
detect: {
js: /'(.*\.js)'/gi
},
replace: {
js: '\'{{filePath}}\','
}
}
}
}
},
browserSync: {
dev: {
bsFiles: {
src : [
'src/main/webapp/**/*.html',
'src/main/webapp/**/*.json',
'src/main/webapp/assets/styles/**/*.css',
'src/main/webapp/scripts/**/*.{js,html}',
'src/main/webapp/assets/images/**/*.{png,jpg,jpeg,gif,webp,svg}',
'tmp/**/*.{css,js}'
]
}
},
options: {
watchTask: true,
proxy: {
target: "localhost:8080",
proxyOptions: {
xfwd: true
}
}
}
},
clean: {
dist: {
files: [{
dot: true,
src: [
'.tmp',
'<%= yeoman.dist %>/*',
'!<%= yeoman.dist %>/.git*'
]
}]
},
server: '.tmp'
},
jshint: {
options: {
jshintrc: '.jshintrc'
},
all: [
'Gruntfile.js',
'src/main/webapp/scripts/app.js',
'src/main/webapp/scripts/app/**/*.js',
'src/main/webapp/scripts/components/**/*.js'
]
},
concat: {
// src and dest is configured in a subtask called "generated" by usemin
},
uglifyjs: {
// src and dest is configured in a subtask called "generated" by usemin
},
rev: {
dist: {
files: {
src: [
'<%= yeoman.dist %>/scripts/**/*.js',
'<%= yeoman.dist %>/assets/styles/**/*.css',
'<%= yeoman.dist %>/assets/images/**/*.{png,jpg,jpeg,gif,webp,svg}',
'<%= yeoman.dist %>/assets/fonts/*'
]
}
}
},
useminPrepare: {
html: 'src/main/webapp/index.html',
options: {
dest: '<%= yeoman.dist %>',
flow: {
html: {
steps: {
js: ['concat', 'uglifyjs'],
css: ['cssmin', useminAutoprefixer] // Let cssmin concat files so it corrects relative paths to fonts and images
},
post: {}
}
}
}
},
usemin: {
html: ['<%= yeoman.dist %>/**/*.html'],
css: ['<%= yeoman.dist %>/assets/styles/**/*.css'],
js: ['<%= yeoman.dist %>/scripts/**/*.js'],
options: {
assetsDirs: ['<%= yeoman.dist %>', '<%= yeoman.dist %>/assets/styles', '<%= yeoman.dist %>/assets/images', '<%= yeoman.dist %>/assets/fonts'],
patterns: {
js: [
[/(assets\/images\/.*?\.(?:gif|jpeg|jpg|png|webp|svg))/gm, 'Update the JS to reference our revved images']
]
},
dirs: ['<%= yeoman.dist %>']
}
},
imagemin: {
dist: {
files: [{
expand: true,
cwd: 'src/main/webapp/assets/images',
src: '**/*.{jpg,jpeg}', // we don't optimize PNG files as it doesn't work on Linux. If you are not on Linux, feel free to use '**/*.{png,jpg,jpeg}'
dest: '<%= yeoman.dist %>/assets/images'
}]
}
},
svgmin: {
dist: {
files: [{
expand: true,
cwd: 'src/main/webapp/assets/images',
src: '**/*.svg',
dest: '<%= yeoman.dist %>/assets/images'
}]
}
},
cssmin: {
// src and dest is configured in a subtask called "generated" by usemin
},
ngtemplates: {
dist: {
cwd: 'src/main/webapp',
src: ['scripts/app/**/*.html', 'scripts/components/**/*.html',],
dest: '.tmp/templates/templates.js',
options: {
module: 'sampleapplicationApp',
usemin: 'scripts/app.js',
htmlmin: '<%= htmlmin.dist.options %>'
}
}
},
htmlmin: {
dist: {
options: {
removeCommentsFromCDATA: true,
// https://github.com/yeoman/grunt-usemin/issues/44
collapseWhitespace: true,
collapseBooleanAttributes: true,
conservativeCollapse: true,
removeAttributeQuotes: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
keepClosingSlash: true
},
files: [{
expand: true,
cwd: '<%= yeoman.dist %>',
src: ['*.html'],
dest: '<%= yeoman.dist %>'
}]
}
},
// Put files not handled in other tasks here
copy: {
fonts: {
files: [{
expand: true,
dot: true,
flatten: true,
cwd: 'src/main/webapp',
dest: '<%= yeoman.dist %>/assets/fonts',
src: [
'bower_components/bootstrap/fonts/*.*'
]
}]
},
dist: {
files: [{
expand: true,
dot: true,
cwd: 'src/main/webapp',
dest: '<%= yeoman.dist %>',
src: [
'*.html',
'scripts/**/*.html',
'assets/images/**/*.{png,gif,webp,jpg,jpeg,svg}',
'assets/fonts/*'
]
}, {
expand: true,
cwd: '.tmp/assets/images',
dest: '<%= yeoman.dist %>/assets/images',
src: [
'generated/*'
]
}]
}
},
karma: {
unit: {
configFile: 'src/test/javascript/karma.conf.js',
singleRun: true
}
},
ngAnnotate: {
dist: {
files: [{
expand: true,
cwd: '.tmp/concat/scripts',
src: '*.js',
dest: '.tmp/concat/scripts'
}]
}
},
buildcontrol: {
options: {
commit: true,
push: false,
connectCommits: false,
message: 'Built %sourceName% from commit %sourceCommit% on branch %sourceBranch%'
}
},
ngconstant: {
options: {
name: 'sampleapplicationApp',
deps: false,
wrap: '"use strict";\n// DO NOT EDIT THIS FILE, EDIT THE GRUNT TASK NGCONSTANT SETTINGS INSTEAD WHICH GENERATES THIS FILE\n{%= __ngModule %}'
},
dev: {
options: {
dest: 'src/main/webapp/scripts/app/app.constants.js'
},
constants: {
ENV: 'dev',
VERSION: parseVersionFromPomXml()
}
},
prod: {
options: {
dest: '.tmp/scripts/app/app.constants.js'
},
constants: {
ENV: 'prod',
VERSION: parseVersionFromPomXml()
}
}
}
});
grunt.registerTask('serve', [
'clean:server',
'wiredep',
'ngconstant:dev',
'browserSync',
'watch'
]);
grunt.registerTask('server', function (target) {
grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
grunt.task.run([target ? ('serve:' + target) : 'serve']);
});
grunt.registerTask('test', [
'clean:server',
'wiredep:test',
'ngconstant:dev',
'karma'
]);
grunt.registerTask('build', [
'clean:dist',
'wiredep:app',
'ngconstant:prod',
'useminPrepare',
'ngtemplates',
'imagemin',
'svgmin',
'concat',
'copy:fonts',
'copy:dist',
'ngAnnotate',
'cssmin',
'autoprefixer',
'uglify',
'rev',
'usemin',
'htmlmin'
]);
grunt.registerTask('default', ['serve']);
};

View File

@ -2,6 +2,8 @@
This application was generated using JHipster, you can find documentation and help at [https://jhipster.github.io](https://jhipster.github.io).
## Development
Before you can build this project, you must install and configure the following dependencies on your machine:
1. [Node.js][]: We use Node to run a development web server and build the project.
@ -12,25 +14,26 @@ After installing Node, you should be able to run the following command to instal
npm install
We use [Grunt][] as our build system. Install the grunt command-line tool globally with:
We use [Gulp][] as our build system. Install the Gulp command-line tool globally with:
npm install -g grunt-cli
npm install -g gulp
Run the following commands in two separate terminals to create a blissful development experience where your browser
auto-refreshes when files change on your hard drive.
mvn
grunt
./mvnw
gulp
Bower is used to manage CSS and JavaScript dependencies used in this application. You can upgrade dependencies by
specifying a newer version in `bower.json`. You can also run `bower update` and `bower install` to manage dependencies.
Add the `-h` flag on any command to see how you can use it. For example, `bower update -h`.
# Building for production
## Building for production
To optimize the sampleApplication client for production, run:
mvn -Pprod clean package
./mvnw -Pprod clean package
This will concatenate and minify CSS and JavaScript files. It will also modify `index.html` so it references
these new files.
@ -41,15 +44,16 @@ To ensure everything worked, run:
Then navigate to [http://localhost:8080](http://localhost:8080) in your browser.
# Testing
## Testing
Unit tests are run by [Karma][] and written with [Jasmine][]. They're located in `src/test/javascript` and can be run with:
Unit tests are run by [Karma][] and written with [Jasmine][]. They're located in `src/test/javascript/` and can be run with:
grunt test
gulp test
UI end-to-end tests are powered by [Protractor][], which is built on top of WebDriverJS. They're located in `src/test/javascript/e2e`
and can be run by starting Spring Boot in one terminal (`./mvnw spring-boot:run`) and running the tests (`gulp itest`) in a second one.
# Continuous Integration
## Continuous Integration
To setup this project in Jenkins, use the following configuration:
@ -62,13 +66,21 @@ To setup this project in Jenkins, use the following configuration:
* Poll SCM / Schedule: `H/5 * * * *`
* Build
* Invoke Maven / Tasks: `-Pprod clean package`
* Execute Shell / Command:
````
./mvnw spring-boot:run &
bootPid=$!
sleep 30s
gulp itest
kill $bootPid
````
* Post-build Actions
* Publish JUnit test result report / Test Report XMLs: `build/test-results/*.xml`
* Publish JUnit test result report / Test Report XMLs: `build/test-results/*.xml,build/reports/e2e/*.xml`
[JHipster]: https://jhipster.github.io/
[Node.js]: https://nodejs.org/
[Bower]: http://bower.io/
[Grunt]: http://gruntjs.com/
[Gulp]: http://gulpjs.com/
[BrowserSync]: http://www.browsersync.io/
[Karma]: http://karma-runner.github.io/
[Jasmine]: http://jasmine.github.io/2.0/introduction.html

View File

@ -1,49 +1,62 @@
{
"name": "sampleApplication",
"version": "0.0.0",
"appPath": "src/main/webapp",
"name": "sampleApplication",
"appPath": "src/main/webapp/",
"testPath": "src/test/javascript/spec",
"dependencies": {
"angular": "1.4.8",
"angular-aria": "1.4.8",
"angular-bootstrap": "0.14.3",
"angular": "1.5.2",
"angular-aria": "1.5.2",
"angular-bootstrap": "1.2.5",
"angular-cache-buster": "0.4.3",
"angular-cookies": "1.4.8",
"angular-dynamic-locale": "0.1.28",
"angular-i18n": "1.4.8",
"angular-local-storage": "0.2.3",
"angular-loading-bar": "0.8.0",
"angular-resource": "1.4.8",
"angular-sanitize": "1.4.8",
"angular-translate": "2.8.1",
"angular-translate-interpolation-messageformat": "2.8.1",
"angular-translate-loader-partial": "2.8.1",
"angular-translate-storage-cookie": "2.8.1",
"angular-ui-router": "0.2.15",
"bootstrap": "3.3.5",
"jquery": "2.1.4",
"angular-cookies": "1.5.2",
"angular-dynamic-locale": "0.1.30",
"angular-i18n": "1.5.2",
"ngstorage": "0.3.10",
"angular-loading-bar": "0.9.0",
"angular-resource": "1.5.2",
"angular-sanitize": "1.5.2",
"angular-translate": "2.11.0",
"angular-translate-interpolation-messageformat": "2.11.0",
"angular-translate-loader-partial": "2.11.0",
"angular-translate-storage-cookie": "2.11.0",
"angular-ui-router": "0.2.18",
"bootstrap": "3.3.6",
"bootstrap-ui-datetime-picker": "2.2.3",
"jquery": "2.2.2",
"json3": "3.3.2",
"modernizr": "3.2.0",
"ng-file-upload": "10.0.2",
"ngInfiniteScroll": "1.2.1",
"swagger-ui": "2.1.3"
"modernizr": "3.3.1",
"ng-file-upload": "12.0.4",
"ngInfiniteScroll": "1.2.2",
"swagger-ui": "2.1.4"
},
"devDependencies": {
"angular-mocks": "1.4.8",
"angular-scenario": "1.4.8"
"angular-mocks": "1.5.2",
"angular-scenario": "1.5.2"
},
"overrides": {
"angular": {
"dependencies": {
"jquery": "2.2.2"
}
},
"bootstrap": {
"main": [
"dist/js/bootstrap.js",
"dist/css/bootstrap.css",
"less/bootstrap.less"
]
},
"messageformat": {
"main": [
"messageformat.js",
"locale/en.js"
]
}
},
"resolutions": {
"angular": "1.4.8",
"angular-cookies": "1.4.8",
"jquery": "2.1.4"
"angular": "1.5.2",
"angular-cookies": "1.5.2",
"angular-bootstrap": "1.2.5",
"jquery": "2.2.2"
}
}

50
gulp/build.js Normal file
View File

@ -0,0 +1,50 @@
var fs = require('fs'),
gulp = require('gulp'),
lazypipe = require('lazypipe'),
footer = require('gulp-footer'),
sourcemaps = require('gulp-sourcemaps'),
rev = require('gulp-rev'),
htmlmin = require('gulp-htmlmin'),
ngAnnotate = require('gulp-ng-annotate'),
prefix = require('gulp-autoprefixer'),
cssnano = require('gulp-cssnano'),
uglify = require('gulp-uglify'),
useref = require("gulp-useref"),
revReplace = require("gulp-rev-replace")
plumber = require('gulp-plumber'),
gulpIf = require('gulp-if'),
handleErrors = require('./handleErrors');
var config = require('./config');
var initTask = lazypipe()
.pipe(sourcemaps.init)
.pipe(footer, ';');
var jsTask = lazypipe()
.pipe(ngAnnotate)
.pipe(uglify);
var cssTask = lazypipe()
.pipe(prefix)
.pipe(cssnano);
module.exports = function() {
var templates = fs.readFileSync(config.tmp + '/templates.js');
var manifest = gulp.src(config.revManifest);
return gulp.src([config.app + '**/*.html',
'!' + config.app + 'app/**/*.html',
'!' + config.app + 'swagger-ui/**/*',
'!' + config.bower + '**/*.html'])
.pipe(plumber({errorHandler: handleErrors}))
//init sourcemaps and prepend semicolon
.pipe(useref({}, initTask))
//append html templates
.pipe(gulpIf('**/app.js', footer(templates)))
.pipe(gulpIf('*.js', jsTask()))
.pipe(gulpIf('*.css', cssTask()))
.pipe(gulpIf('*.html', htmlmin({collapseWhitespace: true})))
.pipe(gulpIf('**/*.!(html)', rev()))
.pipe(revReplace({manifest}))
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest(config.dist));
}

12
gulp/config.js Normal file
View File

@ -0,0 +1,12 @@
module.exports = {
app: 'src/main/webapp/',
dist: 'target/www/',
test: 'src/test/javascript/',
bower: 'src/main/webapp/bower_components/',
tmp: 'target/tmp',
revManifest: 'target/tmp/rev-manifest.json',
port: 9000,
apiPort: 8080,
liveReloadPort: 35729,
uri: 'http://localhost:'
};

20
gulp/handleErrors.js Normal file
View File

@ -0,0 +1,20 @@
var notify = require("gulp-notify");
var argv = require('yargs').argv;
module.exports = function() {
var args = Array.prototype.slice.call(arguments);
var notification = argv.notification === undefined ? true : argv.notification;
// Send error to notification center with gulp-notify
if(notification) {
notify.onError({
title: "JHipster Gulp Build",
subtitle: "Failure!",
message: "Error: <%= error.message %>",
sound: "Beep"
}).apply(this, args);
}
// Keep gulp from hanging on this task
this.emit('end');
};

60
gulp/serve.js Normal file
View File

@ -0,0 +1,60 @@
var gulp = require('gulp'),
util = require('./utils'),
url = require('url'),
browserSync = require('browser-sync'),
proxy = require('proxy-middleware');
var config = require('./config');
module.exports = function () {
var baseUri = config.uri + config.apiPort;
// Routes to proxy to the backend. Routes ending with a / will setup
// a redirect so that if accessed without a trailing slash, will
// redirect. This is required for some endpoints for proxy-middleware
// to correctly handle them.
var proxyRoutes = [
'/'
];
var requireTrailingSlash = proxyRoutes.filter(function (r) {
return util.endsWith(r, '/');
}).map(function (r) {
// Strip trailing slash so we can use the route to match requests
// with non trailing slash
return r.substr(0, r.length - 1);
});
var proxies = [
// Ensure trailing slash in routes that require it
function (req, res, next) {
requireTrailingSlash.forEach(function (route){
if (url.parse(req.url).path === route) {
res.statusCode = 301;
res.setHeader('Location', route + '/');
res.end();
}
});
next();
}
]
.concat(
// Build a list of proxies for routes: [route1_proxy, route2_proxy, ...]
proxyRoutes.map(function (r) {
var options = url.parse(baseUri + r);
options.route = r;
options.preserveHost = true;
return proxy(options);
}));
browserSync({
open: true,
port: config.port,
server: {
baseDir: config.app,
middleware: proxies
}
});
gulp.start('watch');
}

36
gulp/utils.js Normal file
View File

@ -0,0 +1,36 @@
'use strict';
var fs = require('fs');
module.exports = {
endsWith : endsWith,
parseVersion : parseVersion,
isLintFixed : isLintFixed
}
function endsWith(str, suffix) {
return str.indexOf('/', str.length - suffix.length) !== -1;
};
var parseString = require('xml2js').parseString;
// return the version number from `pom.xml` file
function parseVersion() {
var version;
var pomXml = fs.readFileSync('pom.xml', 'utf8');
parseString(pomXml, function (err, result) {
if (result.project.version && result.project.version[0]) {
version = result.project.version[0];
} else if (result.project.parent && result.project.parent[0] && result.project.parent[0].version && result.project.parent[0].version[0]) {
version = result.project.parent[0].version[0];
} else {
throw new Error('pom.xml is malformed. No version is defined');
}
});
return version;
}
function isLintFixed(file) {
// Has ESLint fixed the file contents?
return file.eslint !== null && file.eslint.fixed;
}

275
gulpfile.js Normal file
View File

@ -0,0 +1,275 @@
// Generated on 2016-03-23 using generator-jhipster 3.0.0
'use strict';
var gulp = require('gulp'),
rev = require('gulp-rev'),
templateCache = require('gulp-angular-templatecache'),
htmlmin = require('gulp-htmlmin'),
imagemin = require('gulp-imagemin'),
ngConstant = require('gulp-ng-constant-fork'),
eslint = require('gulp-eslint'),
argv = require('yargs').argv,
gutil = require('gulp-util'),
protractor = require('gulp-protractor').protractor,
es = require('event-stream'),
flatten = require('gulp-flatten'),
del = require('del'),
wiredep = require('wiredep').stream,
runSequence = require('run-sequence'),
browserSync = require('browser-sync'),
KarmaServer = require('karma').Server,
plumber = require('gulp-plumber'),
changed = require('gulp-changed'),
gulpIf = require('gulp-if'),
inject = require('gulp-inject'),
angularFilesort = require('gulp-angular-filesort');
var handleErrors = require('./gulp/handleErrors'),
serve = require('./gulp/serve'),
util = require('./gulp/utils'),
build = require('./gulp/build');
var yorc = require('./.yo-rc.json')['generator-jhipster'];
var config = require('./gulp/config');
gulp.task('clean', function () {
return del([config.dist], { dot: true });
});
gulp.task('copy', function () {
return es.merge(
gulp.src(config.app + 'i18n/**')
.pipe(plumber({errorHandler: handleErrors}))
.pipe(changed(config.dist + 'i18n/'))
.pipe(gulp.dest(config.dist + 'i18n/')),
gulp.src(config.bower + 'bootstrap/fonts/*.*')
.pipe(plumber({errorHandler: handleErrors}))
.pipe(changed(config.dist + 'content/fonts/'))
.pipe(rev())
.pipe(gulp.dest(config.dist + 'content/fonts/'))
.pipe(rev.manifest(config.revManifest, {
base: config.dist,
merge: true
}))
.pipe(gulp.dest(config.dist)),
gulp.src(config.app + 'content/**/*.{woff,woff2,svg,ttf,eot,otf}')
.pipe(plumber({errorHandler: handleErrors}))
.pipe(changed(config.dist + 'content/fonts/'))
.pipe(flatten())
.pipe(rev())
.pipe(gulp.dest(config.dist + 'content/fonts/'))
.pipe(rev.manifest(config.revManifest, {
base: config.dist,
merge: true
}))
.pipe(gulp.dest(config.dist)),
gulp.src([config.app + 'robots.txt', config.app + 'favicon.ico', config.app + '.htaccess'], { dot: true })
.pipe(plumber({errorHandler: handleErrors}))
.pipe(changed(config.dist))
.pipe(gulp.dest(config.dist))
);
});
gulp.task('images', function () {
return gulp.src(config.app + 'content/images/**')
.pipe(plumber({errorHandler: handleErrors}))
.pipe(changed(config.dist + 'content/images'))
.pipe(imagemin({optimizationLevel: 5, progressive: true, interlaced: true}))
.pipe(rev())
.pipe(gulp.dest(config.dist + 'content/images'))
.pipe(rev.manifest(config.revManifest, {
base: config.dist,
merge: true
}))
.pipe(gulp.dest(config.dist))
.pipe(browserSync.reload({stream: true}));
});
gulp.task('languages', function () {
var locales = yorc.languages.map(function (locale) {
return config.bower + 'angular-i18n/angular-locale_' + locale + '.js';
});
return gulp.src(locales)
.pipe(plumber({errorHandler: handleErrors}))
.pipe(changed(config.app + 'i18n/'))
.pipe(gulp.dest(config.app + 'i18n/'));
});
gulp.task('styles', [], function () {
return gulp.src(config.app + 'content/css')
.pipe(browserSync.reload({stream: true}));
});
gulp.task('inject', function () {
return gulp.src(config.app + 'index.html')
.pipe(inject(gulp.src(config.app + 'app/**/*.js').pipe(angularFilesort()), {relative: true}))
.pipe(gulp.dest(config.app));
});
gulp.task('wiredep', ['wiredep:test', 'wiredep:app']);
gulp.task('wiredep:app', function () {
var stream = gulp.src(config.app + 'index.html')
.pipe(plumber({errorHandler: handleErrors}))
.pipe(wiredep({
exclude: [
/angular-i18n/, // localizations are loaded dynamically
'bower_components/bootstrap/dist/js/' // exclude bootstrap js files as we use ui-bootstrap
]
}))
.pipe(gulp.dest(config.app));
return stream;
});
gulp.task('wiredep:test', function () {
return gulp.src(config.test + 'karma.conf.js')
.pipe(plumber({errorHandler: handleErrors}))
.pipe(wiredep({
exclude: [
/angular-i18n/, // localizations are loaded dynamically
/angular-scenario/,
'bower_components/bootstrap/dist/js/' // exclude Bootstrap js files as we use ui-bootstrap
],
ignorePath: /\.\.\/\.\.\//, // remove ../../ from paths of injected JavaScript files
devDependencies: true,
fileTypes: {
js: {
block: /(([\s\t]*)\/\/\s*bower:*(\S*))(\n|\r|.)*?(\/\/\s*endbower)/gi,
detect: {
js: /'(.*\.js)'/gi
},
replace: {
js: '\'src/{{filePath}}\','
}
}
}
}))
.pipe(gulp.dest(config.test));
});
gulp.task('assets:prod', ['images', 'styles', 'html'], build);
gulp.task('html', function () {
return gulp.src(config.app + 'app/**/*.html')
.pipe(htmlmin({collapseWhitespace: true}))
.pipe(templateCache({
module: 'sampleApplicationApp',
root: 'app/',
moduleSystem: 'IIFE'
}))
.pipe(gulp.dest(config.tmp));
});
gulp.task('ngconstant:dev', function () {
return ngConstant({
dest: 'app.constants.js',
name: 'sampleApplicationApp',
deps: false,
noFile: true,
interpolate: /\{%=(.+?)%\}/g,
wrap:
'(function () {\n' +
' "use strict";\n' +
' // DO NOT EDIT THIS FILE, EDIT THE GULP TASK NGCONSTANT SETTINGS INSTEAD WHICH GENERATES THIS FILE\n' +
' {%= __ngModule %}\n' +
'})();\n',
constants: {
ENV: 'dev',
VERSION: util.parseVersion()
}
})
.pipe(gulp.dest(config.app + 'app/'));
});
gulp.task('ngconstant:prod', function () {
return ngConstant({
dest: 'app.constants.js',
name: 'sampleApplicationApp',
deps: false,
noFile: true,
interpolate: /\{%=(.+?)%\}/g,
wrap:
'(function () {\n' +
' "use strict";\n' +
' // DO NOT EDIT THIS FILE, EDIT THE GULP TASK NGCONSTANT SETTINGS INSTEAD WHICH GENERATES THIS FILE\n' +
' {%= __ngModule %}\n' +
'})();\n',
constants: {
ENV: 'prod',
VERSION: util.parseVersion()
}
})
.pipe(gulp.dest(config.app + 'app/'));
});
// check app for eslint errors
gulp.task('eslint', function () {
return gulp.src(['gulpfile.js', config.app + 'app/**/*.js'])
.pipe(plumber({errorHandler: handleErrors}))
.pipe(eslint())
.pipe(eslint.format())
.pipe(eslint.failOnError());
});
// check app for eslint errors anf fix some of them
gulp.task('eslint:fix', function () {
return gulp.src(config.app + 'app/**/*.js')
.pipe(plumber({errorHandler: handleErrors}))
.pipe(eslint({
fix: true
}))
.pipe(eslint.format())
.pipe(gulpIf(util.isLintFixed, gulp.dest(config.app + 'app')));
});
gulp.task('test', ['wiredep:test', 'ngconstant:dev'], function (done) {
new KarmaServer({
configFile: __dirname + '/' + config.test + 'karma.conf.js',
singleRun: true
}, done).start();
});
/* to run individual suites pass `gulp itest --suite suiteName` */
gulp.task('protractor', function () {
var configObj = {
configFile: config.test + 'protractor.conf.js'
};
if (argv.suite) {
configObj['args'] = ['--suite', argv.suite];
}
return gulp.src([])
.pipe(plumber({errorHandler: handleErrors}))
.pipe(protractor(configObj))
.on('error', function () {
gutil.log('E2E Tests failed');
process.exit(1);
});
});
gulp.task('itest', ['protractor']);
gulp.task('watch', function () {
gulp.watch('bower.json', ['install']);
gulp.watch(['gulpfile.js', 'pom.xml'], ['ngconstant:dev']);
gulp.watch(config.app + 'content/css/**/*.css', ['styles']);
gulp.watch(config.app + 'content/images/**', ['images']);
gulp.watch(config.app + 'app/**/*.js', ['inject']);
gulp.watch([config.app + '*.html', config.app + 'app/**', config.app + 'i18n/**']).on('change', browserSync.reload);
});
gulp.task('install', function () {
runSequence(['wiredep', 'ngconstant:dev'], 'languages', 'inject');
});
gulp.task('serve', function () {
runSequence('install', serve);
});
gulp.task('build', ['clean'], function (cb) {
runSequence(['copy', 'wiredep:app', 'ngconstant:prod', 'languages'], 'inject', 'assets:prod', cb);
});
gulp.task('default', ['serve']);

View File

@ -1,5 +1,5 @@
{
"name": "sampleapplication",
"name": "sample-application",
"version": "0.0.0",
"description": "Description for sampleApplication",
"private": true,
@ -11,57 +11,62 @@
"bower": "^1.6.3"
},
"devDependencies": {
"grunt": "0.4.5",
"grunt-autoprefixer": "3.0.3",
"grunt-build-control": "0.6.1",
"grunt-wiredep": "2.0.0",
"grunt-browser-sync": "2.2.0",
"browser-sync": "2.10.0",
"grunt-contrib-copy": "0.8.2",
"grunt-contrib-clean": "0.7.0",
"grunt-contrib-concat": "0.5.1",
"grunt-contrib-cssmin": "0.14.0",
"grunt-contrib-htmlmin": "0.6.0",
"grunt-contrib-imagemin": "1.0.0",
"grunt-contrib-jshint": "0.11.3",
"grunt-contrib-uglify": "0.10.1",
"grunt-contrib-watch": "0.6.1",
"grunt-modernizr": "1.0.1",
"grunt-ng-annotate": "1.0.1",
"grunt-ng-constant": "1.1.0",
"grunt-rev": "0.1.0",
"grunt-svgmin": "3.1.0",
"grunt-text-replace": "0.4.0",
"grunt-usemin": "3.1.1",
"grunt-angular-templates":"0.5.7",
"load-grunt-tasks": "3.3.0",
"grunt-karma": "0.12.1",
"time-grunt": "1.2.2",
"browser-sync": "2.11.2",
"del": "2.2.0",
"eslint-config-angular": "0.5.0",
"eslint-plugin-angular": "1.0.0",
"event-stream": "3.3.2",
"jshint-stylish": "2.1.0",
"karma-script-launcher": "0.1.0",
"karma-chrome-launcher": "0.2.1",
"karma-html2js-preprocessor": "0.1.0",
"karma-jasmine": "0.3.6",
"karma-requirejs": "0.2.2",
"karma-phantomjs-launcher": "0.2.1",
"phantomjs": "1.9.18",
"karma": "0.13.19",
"karma-coverage": "0.5.3",
"generator-jhipster": "3.0.0",
"gulp": "3.9.1",
"gulp-angular-filesort": "1.1.1",
"gulp-angular-templatecache": "1.8.0",
"gulp-autoprefixer": "3.1.0",
"gulp-changed": "1.3.0",
"gulp-cssnano": "2.1.1",
"gulp-eslint": "2.0.0",
"gulp-flatten": "0.2.0",
"gulp-footer": "1.0.5",
"gulp-htmlmin": "1.3.0",
"gulp-if": "2.0.0",
"gulp-imagemin": "2.4.0",
"gulp-inject": "3.0.0",
"gulp-ng-annotate": "2.0.0",
"gulp-ng-constant-fork": "0.4.1",
"gulp-notify": "2.2.0",
"gulp-plumber": "1.1.0",
"gulp-protractor": "2.1.0",
"gulp-util": "3.0.7",
"gulp-rev": "7.0.0",
"gulp-rev-replace": "^0.4.3",
"gulp-sourcemaps": "1.6.0",
"gulp-uglify": "1.5.3",
"gulp-useref": "3.0.7",
"jasmine-core": "2.4.1",
"jasmine-reporters": "2.1.1",
"karma": "0.13.22",
"karma-chrome-launcher": "0.2.3",
"karma-coverage": "0.5.5",
"karma-jasmine": "0.3.8",
"karma-jenkins-reporter": "0.0.2",
"generator-jhipster": "2.27.2",
"lodash": "3.10.1",
"xml2js": "0.4.15",
"yo": ">=1.3.0",
"requirejs": "2.1",
"jasmine-core": "2.3.4",
"zeparser": "0.0.7",
"wiredep": "2.2.2"
"karma-phantomjs-launcher": "1.0.0",
"karma-script-launcher": "0.2.0",
"lazypipe": "1.0.1",
"lodash": "4.6.1",
"map-stream": "0.0.6",
"phantomjs-prebuilt": "2.1.4",
"protractor": "3.1.1",
"protractor-jasmine2-screenshot-reporter": "0.3.0",
"proxy-middleware": "0.15.0",
"run-sequence": "1.1.5",
"wiredep": "4.0.0",
"xml2js": "0.4.16",
"yargs": "4.3.2"
},
"engines": {
"node": "^0.12 || ^4.2"
"node": "^4.3"
},
"scripts": {
"test": "grunt test"
"test": "gulp test",
"postinstall": "webdriver-manager update"
}
}

163
pom.xml
View File

@ -5,12 +5,12 @@
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>1.3.1.RELEASE</version>
<version>1.3.3.RELEASE</version>
<relativePath/>
</parent>
<groupId>com.mycompany.myapp</groupId>
<artifactId>sampleapplication</artifactId>
<artifactId>sample-application</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>sampleApplication</name>
@ -33,9 +33,9 @@
</prerequisites>
<properties>
<hikaricp.version>2.4.1</hikaricp.version>
<assertj-core.version>3.1.0</assertj-core.version>
<awaitility.version>1.4.0</awaitility.version>
<hikaricp.version>2.4.3</hikaricp.version>
<assertj-core.version>3.3.0</assertj-core.version>
<awaitility.version>1.7.0</awaitility.version>
<commons-io.version>2.4</commons-io.version>
<commons-lang.version>2.6</commons-lang.version>
<gatling.version>2.1.7</gatling.version>
@ -50,18 +50,19 @@
<liquibase-hibernate4.version>3.5</liquibase-hibernate4.version>
<mapstruct.version>1.0.0.Final</mapstruct.version>
<maven.build.timestamp.format>yyyyMMddHHmmss</maven.build.timestamp.format>
<maven-enforcer-plugin.version>1.3.1</maven-enforcer-plugin.version>
<maven-sortpom-plugin.version>2.3.0</maven-sortpom-plugin.version>
<maven-enforcer-plugin.version>1.4.1</maven-enforcer-plugin.version>
<sortpom-maven-plugin.version>2.5.0</sortpom-maven-plugin.version>
<metrics-spark-reporter.version>1.2</metrics-spark-reporter.version>
<metrics-spring.version>3.1.2</metrics-spring.version>
<metrics-spring.version>3.1.3</metrics-spring.version>
<logstash-logback-encoder.version>4.6</logstash-logback-encoder.version>
<run.addResources>false</run.addResources>
<spring-security.version>4.0.2.RELEASE</spring-security.version>
<springfox.version>2.0.3</springfox.version>
<spring-security.version>4.0.4.RELEASE</spring-security.version>
<springfox.version>2.4.0</springfox.version>
<!-- Sonar properties -->
<project.testresult.directory>${project.build.directory}/test-results</project.testresult.directory>
<sonar-maven-plugin.version>2.6</sonar-maven-plugin.version>
<jacoco-maven-plugin.version>0.7.4.201502262128</jacoco-maven-plugin.version>
<sonar.exclusions>src/main/webapp/assets/**/*.*, src/main/webapp/bower_components/**/*.*, src/main/webapp/dist/**/*.*</sonar.exclusions>
<sonar-maven-plugin.version>2.7.1</sonar-maven-plugin.version>
<jacoco-maven-plugin.version>0.7.6.201602180812</jacoco-maven-plugin.version>
<sonar.exclusions>src/main/webapp/content/**/*.*, src/main/webapp/bower_components/**/*.*, target/www/**/*.*</sonar.exclusions>
<sonar.java.codeCoveragePlugin>jacoco</sonar.java.codeCoveragePlugin>
<sonar.jacoco.itReportPath>${project.testresult.directory}/coverage/jacoco/jacoco-it.exec</sonar.jacoco.itReportPath>
<sonar.jacoco.reportPath>${project.testresult.directory}/coverage/jacoco/jacoco.exec</sonar.jacoco.reportPath>
@ -204,12 +205,13 @@
</exclusions>
</dependency>
<!-- The HikariCP Java Agent is disabled by default, as it is experimental
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP-agent</artifactId>
<version>${HikariCP.version}</version>
</dependency>
-->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP-agent</artifactId>
<version>${HikariCP.version}</version>
</dependency>
-->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
@ -336,6 +338,25 @@
</exclusions>
</dependency>
<!-- Spring Cloud -->
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>${logstash-logback-encoder.version}</version>
<exclusions>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</exclusion>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</exclusion>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-access</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-cloudfoundry-connector</artifactId>
@ -365,14 +386,14 @@
<defaultGoal>spring-boot:run</defaultGoal>
<resources>
<resource>
<directory>src/main/resources</directory>
<directory>src/main/resources/</directory>
<filtering>true</filtering>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<directory>src/main/resources/</directory>
<filtering>false</filtering>
<excludes>
<exclude>**/*.xml</exclude>
@ -381,9 +402,9 @@
</resources>
<plugins>
<plugin>
<groupId>com.google.code.sortpom</groupId>
<artifactId>maven-sortpom-plugin</artifactId>
<version>${maven-sortpom-plugin.version}</version>
<groupId>com.github.ekryd.sortpom</groupId>
<artifactId>sortpom-maven-plugin</artifactId>
<version>${sortpom-maven-plugin.version}</version>
<executions>
<execution>
<phase>verify</phase>
@ -453,18 +474,11 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>-Xmx256m ${surefireArgLine}</argLine>
<argLine>-Djava.security.egd=file:/dev/./urandom -Xmx256m ${surefireArgLine}</argLine>
<!-- Force alphabetical order to have a reproducible build -->
<runOrder>alphabetical</runOrder>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<packagingExcludes>WEB-INF/lib/tomcat-*.jar</packagingExcludes>
</configuration>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
@ -546,7 +560,7 @@
<defaultSchemaName></defaultSchemaName>
<username></username>
<password></password>
<referenceUrl>hibernate:spring:com.mycompany.myapp.domain?dialect=</referenceUrl>
<referenceUrl>hibernate:spring:com.mycompany.myapp.domain?dialect=&amp;hibernate.ejb.naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringNamingStrategy</referenceUrl>
<verbose>true</verbose>
<logging>debug</logging>
</configuration>
@ -573,12 +587,31 @@
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<executable>true</executable>
<!--
Enable the line below to have remote debugging of your application on port 5005
<jvmArguments>-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005</jvmArguments>
-->
<arguments>
<argument>--spring.profiles.active=dev</argument>
</arguments>
</configuration>
</plugin>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.4.1</version>
<configuration>
<imageName>sampleapplication</imageName>
<dockerDirectory>src/main/docker</dockerDirectory>
<resources>
<resource>
<targetPath>/</targetPath>
<directory>${project.build.directory}</directory>
<include>${project.build.finalName}.war</include>
</resource>
</resources>
</configuration>
</plugin>
<!-- jhipster-needle-maven-add-plugin -->
</plugins>
<pluginManagement>
@ -612,6 +645,23 @@
<ignore/>
</action>
</pluginExecution>
<pluginExecution>
<pluginExecutionFilter>
<groupId>com.github.trecloux</groupId>
<artifactId>
yeoman-maven-plugin
</artifactId>
<versionRange>
[0.5,)
</versionRange>
<goals>
<goal>build</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore/>
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
@ -641,34 +691,14 @@
<optional>true</optional>
</dependency>
</dependencies>
</profile>
<profile>
<id>fast</id>
<properties>
<!-- log configuration -->
<logback.loglevel>DEBUG</logback.loglevel>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<executable>true</executable>
<arguments>
<argument>--spring.profiles.active=dev,fast</argument>
</arguments>
<warSourceDirectory>src/main/webapp/</warSourceDirectory>
<packagingExcludes>WEB-INF/lib/tomcat-*.jar</packagingExcludes>
</configuration>
</plugin>
</plugins>
@ -687,7 +717,7 @@
<plugin>
<groupId>com.github.trecloux</groupId>
<artifactId>yeoman-maven-plugin</artifactId>
<version>0.4</version>
<version>0.5</version>
<executions>
<execution>
<id>run-frontend-build</id>
@ -696,8 +726,8 @@
<goal>build</goal>
</goals>
<configuration>
<buildTool>grunt</buildTool>
<buildArgs>build --no-color</buildArgs>
<buildTool>gulp</buildTool>
<buildArgs>build --no-notification</buildArgs>
</configuration>
</execution>
</executions>
@ -711,10 +741,7 @@
<configuration>
<filesets>
<fileset>
<directory>src/main/webapp/dist</directory>
</fileset>
<fileset>
<directory>.tmp</directory>
<directory>target/www/</directory>
</fileset>
<fileset>
<directory>node_modules</directory>
@ -722,6 +749,14 @@
</filesets>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<warSourceDirectory>target/www/</warSourceDirectory>
<packagingExcludes>WEB-INF/lib/tomcat-*.jar</packagingExcludes>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>

View File

@ -0,0 +1,10 @@
FROM java:openjdk-8-jdk-alpine
# add directly the war
ADD *.war /app.war
RUN sh -c 'touch /app.war'
VOLUME /tmp
CMD ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.war"]
EXPOSE 8080

15
src/main/docker/app.yml Normal file
View File

@ -0,0 +1,15 @@
version: '2'
services:
sampleapplication-app:
image: sampleapplication
external_links:
- sampleapplication-mysql:mysql
environment:
- SPRING_PROFILES_ACTIVE=prod
- SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/sampleapplication?useUnicode=true&characterEncoding=utf8&useSSL=false
ports:
- 8080:8080
sampleapplication-mysql:
extends:
file: mysql.yml
service: sampleapplication-mysql

14
src/main/docker/mysql.yml Normal file
View File

@ -0,0 +1,14 @@
version: '2'
services:
sampleapplication-mysql:
container_name: sampleapplication-mysql
image: mysql:5.7.11
# volumes:
# - ~/volumes/jhipster/sampleApplication/mysql/:/var/lib/mysql/
environment:
- MYSQL_USER=root
- MYSQL_ALLOW_EMPTY_PASSWORD=yes
- MYSQL_DATABASE=sampleapplication
ports:
- 3306:3306
command: mysqld --lower_case_table_names=1

View File

@ -1,12 +0,0 @@
sampleapplication-mysql:
container_name: sampleapplication-mysql
image: mysql:5.7.9
# volumes:
# - ~/volumes/jhipster/sampleApplication/mysql/:/var/lib/mysql/
environment:
- MYSQL_USER=root
- MYSQL_ALLOW_EMPTY_PASSWORD=yes
- MYSQL_DATABASE=sampleapplication
ports:
- "3306:3306"
command: mysqld --lower_case_table_names=1

View File

@ -1,6 +1,8 @@
sampleapplication-sonar:
container_name: sampleapplication-sonar
image: sonarqube:4.5.6
ports:
- "9000:9000"
- "9092:9092"
version: '2'
services:
sampleapplication-sonar:
container_name: sampleapplication-sonar
image: sonarqube:5.3
ports:
- 9000:9000
- 9092:9092

View File

@ -17,7 +17,7 @@ public class ApplicationWebXml extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.profiles(addDefaultProfile())
.sources(Application.class);
.sources(SampleApplicationApp.class);
}
/**

View File

@ -6,8 +6,7 @@ import com.mycompany.myapp.config.JHipsterProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.actuate.autoconfigure.MetricFilterAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.MetricRepositoryAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.*;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@ -17,7 +16,6 @@ import org.springframework.core.env.SimpleCommandLinePropertySource;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
@ -26,24 +24,22 @@ import java.util.Collection;
@ComponentScan
@EnableAutoConfiguration(exclude = { MetricFilterAutoConfiguration.class, MetricRepositoryAutoConfiguration.class })
@EnableConfigurationProperties({ JHipsterProperties.class, LiquibaseProperties.class })
public class Application {
public class SampleApplicationApp {
private static final Logger log = LoggerFactory.getLogger(Application.class);
private static final Logger log = LoggerFactory.getLogger(SampleApplicationApp.class);
@Inject
private Environment env;
/**
* Initializes sampleApplication.
* <p/>
* Spring profiles can be configured with a program arguments --spring.profiles.active=your-active-profile
* <p/>
* <p>
* You can find more information on how profiles work with JHipster on <a href="http://jhipster.github.io/profiles.html">http://jhipster.github.io/profiles.html</a>.
* </p>
* Spring profiles can be configured with a program arguments --spring.profiles.active=your-active-profile
* <p>
* You can find more information on how profiles work with JHipster on <a href="http://jhipster.github.io/profiles/">http://jhipster.github.io/profiles/</a>.
*/
@PostConstruct
public void initApplication() throws IOException {
public void initApplication() {
if (env.getActiveProfiles().length == 0) {
log.warn("No Spring profile configured, running with default configuration");
} else {
@ -53,10 +49,6 @@ public class Application {
log.error("You have misconfigured your application! " +
"It should not run with both the 'dev' and 'prod' profiles at the same time.");
}
if (activeProfiles.contains(Constants.SPRING_PROFILE_PRODUCTION) && activeProfiles.contains(Constants.SPRING_PROFILE_FAST)) {
log.error("You have misconfigured your application! " +
"It should not run with both the 'prod' and 'fast' profiles at the same time.");
}
if (activeProfiles.contains(Constants.SPRING_PROFILE_DEVELOPMENT) && activeProfiles.contains(Constants.SPRING_PROFILE_CLOUD)) {
log.error("You have misconfigured your application! " +
"It should not run with both the 'dev' and 'cloud' profiles at the same time.");
@ -66,15 +58,20 @@ public class Application {
/**
* Main method, used to run the application.
*
* @param args the command line arguments
* @throws UnknownHostException if the local host name could not be resolved into an address
*/
public static void main(String[] args) throws UnknownHostException {
SpringApplication app = new SpringApplication(Application.class);
SpringApplication app = new SpringApplication(SampleApplicationApp.class);
SimpleCommandLinePropertySource source = new SimpleCommandLinePropertySource(args);
addDefaultProfile(app, source);
Environment env = app.run(args).getEnvironment();
log.info("Access URLs:\n----------------------------------------------------------\n\t" +
log.info("\n----------------------------------------------------------\n\t" +
"Application '{}' is running! Access URLs:\n\t" +
"Local: \t\thttp://127.0.0.1:{}\n\t" +
"External: \thttp://{}:{}\n----------------------------------------------------------",
env.getProperty("spring.application.name"),
env.getProperty("server.port"),
InetAddress.getLocalHost().getHostAddress(),
env.getProperty("server.port"));

View File

@ -22,7 +22,7 @@ public class ExceptionHandlingAsyncTaskExecutor implements AsyncTaskExecutor,
@Override
public void execute(Runnable task) {
executor.execute(task);
executor.execute(createWrappedRunnable(task));
}
@Override

View File

@ -33,7 +33,7 @@ public class AsyncConfiguration implements AsyncConfigurer {
executor.setCorePoolSize(jHipsterProperties.getAsync().getCorePoolSize());
executor.setMaxPoolSize(jHipsterProperties.getAsync().getMaxPoolSize());
executor.setQueueCapacity(jHipsterProperties.getAsync().getQueueCapacity());
executor.setThreadNamePrefix("sampleapplication-Executor-");
executor.setThreadNamePrefix("sample-application-Executor-");
return new ExceptionHandlingAsyncTaskExecutor(executor);
}

View File

@ -22,7 +22,6 @@ import java.util.SortedSet;
@Configuration
@EnableCaching
@AutoConfigureAfter(value = { MetricsConfiguration.class, DatabaseConfiguration.class })
@Profile("!" + Constants.SPRING_PROFILE_FAST)
public class CacheConfiguration {
private final Logger log = LoggerFactory.getLogger(CacheConfiguration.class);

View File

@ -5,14 +5,17 @@ package com.mycompany.myapp.config;
*/
public final class Constants {
// Spring profile for development, production and "fast", see http://jhipster.github.io/profiles.html
// Spring profile for development and production, see http://jhipster.github.io/profiles/
public static final String SPRING_PROFILE_DEVELOPMENT = "dev";
public static final String SPRING_PROFILE_PRODUCTION = "prod";
public static final String SPRING_PROFILE_FAST = "fast";
// Spring profile used when deploying with Spring Cloud (used when deploying to CloudFoundry)
public static final String SPRING_PROFILE_CLOUD = "cloud";
// Spring profile used when deploying to Heroku
public static final String SPRING_PROFILE_HEROKU = "heroku";
// Spring profile used to disable swagger
public static final String SPRING_PROFILE_NO_SWAGGER = "no-swagger";
// Spring profile used to disable running liquibase
public static final String SPRING_PROFILE_NO_LIQUIBASE = "no-liquibase";
public static final String SYSTEM_ACCOUNT = "system";

View File

@ -4,7 +4,6 @@ import com.mycompany.myapp.config.liquibase.AsyncSpringLiquibase;
import com.codahale.metrics.MetricRegistry;
import com.fasterxml.jackson.datatype.hibernate4.Hibernate4Module;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import liquibase.integration.spring.SpringLiquibase;
import org.h2.tools.Server;
@ -12,8 +11,10 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContextException;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -43,8 +44,9 @@ public class DatabaseConfiguration {
private MetricRegistry metricRegistry;
@Bean(destroyMethod = "close")
@ConditionalOnExpression("#{!environment.acceptsProfiles('cloud') && !environment.acceptsProfiles('heroku')}")
public DataSource dataSource(DataSourceProperties dataSourceProperties, JHipsterProperties jHipsterProperties) {
@ConditionalOnExpression("#{!environment.acceptsProfiles('" + Constants.SPRING_PROFILE_CLOUD + "') && !environment.acceptsProfiles('" + Constants.SPRING_PROFILE_HEROKU + "')}")
@ConfigurationProperties(prefix = "spring.datasource.hikari")
public DataSource dataSource(DataSourceProperties dataSourceProperties) {
log.debug("Configuring Datasource");
if (dataSourceProperties.getUrl() == null) {
log.error("Your database connection pool configuration is incorrect! The application" +
@ -53,34 +55,26 @@ public class DatabaseConfiguration {
throw new ApplicationContextException("Database connection pool is not configured correctly");
}
HikariConfig config = new HikariConfig();
config.setDataSourceClassName(dataSourceProperties.getDriverClassName());
config.addDataSourceProperty("url", dataSourceProperties.getUrl());
if (dataSourceProperties.getUsername() != null) {
config.addDataSourceProperty("user", dataSourceProperties.getUsername());
} else {
config.addDataSourceProperty("user", ""); // HikariCP doesn't allow null user
}
if (dataSourceProperties.getPassword() != null) {
config.addDataSourceProperty("password", dataSourceProperties.getPassword());
} else {
config.addDataSourceProperty("password", ""); // HikariCP doesn't allow null password
}
HikariDataSource hikariDataSource = (HikariDataSource) DataSourceBuilder
.create(dataSourceProperties.getClassLoader())
.type(HikariDataSource.class)
.driverClassName(dataSourceProperties.getDriverClassName())
.url(dataSourceProperties.getUrl())
.username(dataSourceProperties.getUsername())
.password(dataSourceProperties.getPassword())
.build();
//MySQL optimizations, see https://github.com/brettwooldridge/HikariCP/wiki/MySQL-Configuration
if ("com.mysql.jdbc.jdbc2.optional.MysqlDataSource".equals(dataSourceProperties.getDriverClassName())) {
config.addDataSourceProperty("cachePrepStmts", jHipsterProperties.getDatasource().isCachePrepStmts());
config.addDataSourceProperty("prepStmtCacheSize", jHipsterProperties.getDatasource().getPrepStmtCacheSize());
config.addDataSourceProperty("prepStmtCacheSqlLimit", jHipsterProperties.getDatasource().getPrepStmtCacheSqlLimit());
}
if (metricRegistry != null) {
config.setMetricRegistry(metricRegistry);
hikariDataSource.setMetricRegistry(metricRegistry);
}
return new HikariDataSource(config);
return hikariDataSource;
}
/**
* Open the TCP port for the H2 database, so it is available remotely.
*
* @return the H2 database TCP server
* @throws SQLException if the server failed to start
*/
@Bean(initMethod = "start", destroyMethod = "stop")
@Profile(Constants.SPRING_PROFILE_DEVELOPMENT)
@ -99,18 +93,13 @@ public class DatabaseConfiguration {
liquibase.setContexts(liquibaseProperties.getContexts());
liquibase.setDefaultSchema(liquibaseProperties.getDefaultSchema());
liquibase.setDropFirst(liquibaseProperties.isDropFirst());
liquibase.setShouldRun(liquibaseProperties.isEnabled());
if (env.acceptsProfiles(Constants.SPRING_PROFILE_FAST)) {
if ("org.h2.jdbcx.JdbcDataSource".equals(dataSourceProperties.getDriverClassName())) {
liquibase.setShouldRun(true);
log.warn("Using '{}' profile with H2 database in memory is not optimal, you should consider switching to" +
" MySQL or Postgresql to avoid rebuilding your database upon each start.", Constants.SPRING_PROFILE_FAST);
} else {
liquibase.setShouldRun(false);
}
if (env.acceptsProfiles(Constants.SPRING_PROFILE_NO_LIQUIBASE)) {
liquibase.setShouldRun(false);
} else {
liquibase.setShouldRun(liquibaseProperties.isEnabled());
log.debug("Configuring Liquibase");
}
return liquibase;
}

View File

@ -5,6 +5,7 @@ import javax.validation.constraints.NotNull;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.web.cors.CorsConfiguration;
/**
* Properties specific to JHipster.
*
@ -19,8 +20,6 @@ public class JHipsterProperties {
private final Http http = new Http();
private final Datasource datasource = new Datasource();
private final Cache cache = new Cache();
private final Mail mail = new Mail();
@ -33,8 +32,6 @@ public class JHipsterProperties {
private final CorsConfiguration cors = new CorsConfiguration();
public Async getAsync() {
return async;
}
@ -43,10 +40,6 @@ public class JHipsterProperties {
return http;
}
public Datasource getDatasource() {
return datasource;
}
public Cache getCache() {
return cache;
}
@ -71,7 +64,6 @@ public class JHipsterProperties {
return cors;
}
public static class Async {
private int corePoolSize = 2;
@ -115,7 +107,7 @@ public class JHipsterProperties {
public static class Cache {
private int timeToLiveInDays = 31;
private int timeToLiveInDays = 1461;
public int getTimeToLiveInDays() {
return timeToLiveInDays;
@ -127,49 +119,6 @@ public class JHipsterProperties {
}
}
public static class Datasource {
private boolean cachePrepStmts = true;
private int prepStmtCacheSize = 250;
private int prepStmtCacheSqlLimit = 2048;
private boolean useServerPrepStmts = true;
public boolean isCachePrepStmts() {
return cachePrepStmts;
}
public void setCachePrepStmts(boolean cachePrepStmts) {
this.cachePrepStmts = cachePrepStmts;
}
public int getPrepStmtCacheSize() {
return prepStmtCacheSize;
}
public void setPrepStmtCacheSize(int prepStmtCacheSize) {
this.prepStmtCacheSize = prepStmtCacheSize;
}
public int getPrepStmtCacheSqlLimit() {
return prepStmtCacheSqlLimit;
}
public void setPrepStmtCacheSqlLimit(int prepStmtCacheSqlLimit) {
this.prepStmtCacheSqlLimit = prepStmtCacheSqlLimit;
}
public boolean isUseServerPrepStmts() {
return useServerPrepStmts;
}
public void setUseServerPrepStmts(boolean useServerPrepStmts) {
this.useServerPrepStmts = useServerPrepStmts;
}
}
public static class Cache {
private int timeToLiveSeconds = 3600;
@ -217,13 +166,13 @@ public class JHipsterProperties {
public static class Security {
private final Rememberme rememberme = new Rememberme();
private final RememberMe rememberMe = new RememberMe();
public Rememberme getRememberme() {
return rememberme;
public RememberMe getRememberMe() {
return rememberMe;
}
public static class Rememberme {
public static class RememberMe {
@NotNull
private String key;
@ -248,7 +197,11 @@ public class JHipsterProperties {
private String termsOfServiceUrl;
private String contact;
private String contactName;
private String contactUrl;
private String contactEmail;
private String license;
@ -286,12 +239,28 @@ public class JHipsterProperties {
this.termsOfServiceUrl = termsOfServiceUrl;
}
public String getContact() {
return contact;
public String getContactName() {
return contactName;
}
public void setContact(String contact) {
this.contact = contact;
public void setContactName(String contactName) {
this.contactName = contactName;
}
public String getContactUrl() {
return contactUrl;
}
public void setContactUrl(String contactUrl) {
this.contactUrl = contactUrl;
}
public String getContactEmail() {
return contactEmail;
}
public void setContactEmail(String contactEmail) {
this.contactEmail = contactEmail;
}
public String getLicense() {
@ -319,6 +288,8 @@ public class JHipsterProperties {
private final Graphite graphite = new Graphite();
private final Logs logs = new Logs();
public Jmx getJmx() {
return jmx;
}
@ -331,6 +302,11 @@ public class JHipsterProperties {
return graphite;
}
public Logs getLogs() {
return logs;
}
public static class Jmx {
private boolean enabled = true;
@ -419,5 +395,67 @@ public class JHipsterProperties {
this.prefix = prefix;
}
}
public static class Logs {
private boolean enabled = false;
private long reportFrequency = 60;
public long getReportFrequency() {
return reportFrequency;
}
public void setReportFrequency(int reportFrequency) {
this.reportFrequency = reportFrequency;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
}
private final Logging logging = new Logging();
public Logging getLogging() { return logging; }
public static class Logging {
private final Logstash logstash = new Logstash();
public Logstash getLogstash() { return logstash; }
public static class Logstash {
private boolean enabled = false;
private String host = "localhost";
private int port = 5000;
private int queueSize = 512;
public boolean isEnabled() { return enabled; }
public void setEnabled(boolean enabled) { this.enabled = enabled; }
public String getHost() { return host; }
public void setHost(String host) { this.host = host; }
public int getPort() { return port; }
public void setPort(int port) { this.port = port; }
public int getQueueSize() { return queueSize; }
public void setQueueSize(int queueSize) { this.queueSize = queueSize; }
}
}
}

View File

@ -4,10 +4,8 @@ import com.mycompany.myapp.config.locale.AngularCookieLocaleResolver;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.core.env.Environment;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
@ -31,15 +29,6 @@ public class LocaleConfiguration extends WebMvcConfigurerAdapter implements Envi
return cookieLocaleResolver;
}
@Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:/i18n/messages");
messageSource.setDefaultEncoding("UTF-8");
messageSource.setCacheSeconds(propertyResolver.getProperty("cache-seconds", Integer.class, -1));
return messageSource;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();

View File

@ -0,0 +1,62 @@
package com.mycompany.myapp.config;
import ch.qos.logback.classic.AsyncAppender;
import ch.qos.logback.classic.LoggerContext;
import net.logstash.logback.appender.LogstashSocketAppender;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
@Configuration
public class LoggingConfiguration {
private final Logger log = LoggerFactory.getLogger(LoggingConfiguration.class);
private LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
@Value("${spring.application.name}")
private String appName;
@Value("${server.port}")
private String serverPort;
@Inject
private JHipsterProperties jHipsterProperties;
@PostConstruct
private void init() {
if (jHipsterProperties.getLogging().getLogstash().isEnabled()) {
addLogstashAppender();
}
}
public void addLogstashAppender() {
log.info("Initializing Logstash logging");
LogstashSocketAppender logstashAppender = new LogstashSocketAppender();
logstashAppender.setName("LOGSTASH");
logstashAppender.setContext(context);
String customFields = "{\"app_name\":\"" + appName + "\",\"app_port\":\"" + serverPort + "\"}";
// Set the Logstash appender config from JHipster properties
logstashAppender.setSyslogHost(jHipsterProperties.getLogging().getLogstash().getHost());
logstashAppender.setPort(jHipsterProperties.getLogging().getLogstash().getPort());
logstashAppender.setCustomFields(customFields);
logstashAppender.start();
// Wrap the appender in an Async appender for performance
AsyncAppender asyncLogstashAppender = new AsyncAppender();
asyncLogstashAppender.setContext(context);
asyncLogstashAppender.setName("ASYNC_LOGSTASH");
asyncLogstashAppender.setQueueSize(jHipsterProperties.getLogging().getLogstash().getQueueSize());
asyncLogstashAppender.addAppender(logstashAppender);
asyncLogstashAppender.start();
context.getLogger("ROOT").addAppender(asyncLogstashAppender);
}
}

View File

@ -2,6 +2,7 @@ package com.mycompany.myapp.config;
import com.codahale.metrics.JmxReporter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Slf4jReporter;
import com.codahale.metrics.graphite.Graphite;
import com.codahale.metrics.graphite.GraphiteReporter;
import com.codahale.metrics.health.HealthCheckRegistry;
@ -22,7 +23,6 @@ import java.util.concurrent.TimeUnit;
@Configuration
@EnableMetrics(proxyTargetClass = true)
@Profile("!" + Constants.SPRING_PROFILE_FAST)
public class MetricsConfiguration extends MetricsConfigurerAdapter {
private static final String PROP_METRIC_REG_JVM_MEMORY = "jvm.memory";
@ -65,11 +65,20 @@ public class MetricsConfiguration extends MetricsConfigurerAdapter {
JmxReporter jmxReporter = JmxReporter.forRegistry(metricRegistry).build();
jmxReporter.start();
}
if (jHipsterProperties.getMetrics().getLogs().isEnabled()) {
log.info("Initializing Metrics Log reporting");
final Slf4jReporter reporter = Slf4jReporter.forRegistry(metricRegistry)
.outputTo(LoggerFactory.getLogger("metrics"))
.convertRatesTo(TimeUnit.SECONDS)
.convertDurationsTo(TimeUnit.MILLISECONDS)
.build();
reporter.start(jHipsterProperties.getMetrics().getLogs().getReportFrequency(), TimeUnit.SECONDS);
}
}
@Configuration
@ConditionalOnClass(Graphite.class)
@Profile("!" + Constants.SPRING_PROFILE_FAST)
public static class GraphiteRegistry {
private final Logger log = LoggerFactory.getLogger(GraphiteRegistry.class);
@ -100,7 +109,6 @@ public class MetricsConfiguration extends MetricsConfigurerAdapter {
@Configuration
@ConditionalOnClass(SparkReporter.class)
@Profile("!" + Constants.SPRING_PROFILE_FAST)
public static class SparkRegistry {
private final Logger log = LoggerFactory.getLogger(SparkRegistry.class);

View File

@ -2,9 +2,11 @@ package com.mycompany.myapp.config;
import com.mycompany.myapp.security.*;
import com.mycompany.myapp.web.filter.CsrfCookieGeneratorFilter;
import com.mycompany.myapp.config.JHipsterProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@ -27,7 +29,7 @@ import javax.inject.Inject;
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Inject
private Environment env;
private JHipsterProperties jHipsterProperties;
@Inject
private AjaxAuthenticationSuccessHandler ajaxAuthenticationSuccessHandler;
@ -62,10 +64,11 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/scripts/**/*.{js,html}")
.antMatchers(HttpMethod.OPTIONS, "/**")
.antMatchers("/app/**/*.{js,html}")
.antMatchers("/bower_components/**")
.antMatchers("/i18n/**")
.antMatchers("/assets/**")
.antMatchers("/content/**")
.antMatchers("/swagger-ui/index.html")
.antMatchers("/test/**")
.antMatchers("/h2-console/**");
@ -84,7 +87,7 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
.rememberMe()
.rememberMeServices(rememberMeServices)
.rememberMeParameter("remember-me")
.key(env.getProperty("jhipster.security.rememberme.key"))
.key(jHipsterProperties.getSecurity().getRememberMe().getKey())
.and()
.formLogin()
.loginProcessingUrl("/api/authentication")
@ -123,7 +126,6 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
.antMatchers("/info/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/autoconfig/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/env/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/trace/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/mappings/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/liquibase/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/v2/api-docs/**").permitAll()

View File

@ -3,9 +3,7 @@ package com.mycompany.myapp.config;
import org.apache.commons.lang.CharEncoding;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.*;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
@Configuration
@ -24,14 +22,4 @@ public class ThymeleafConfiguration {
emailTemplateResolver.setOrder(1);
return emailTemplateResolver;
}
@Bean
@Description("Spring mail message resolver")
public MessageSource emailMessageSource() {
log.info("loading non-reloadable mail messages resources");
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:/mails/messages/messages");
messageSource.setDefaultEncoding(CharEncoding.UTF_8);
return messageSource;
}
}

View File

@ -4,7 +4,6 @@ import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.servlet.InstrumentedFilter;
import com.codahale.metrics.servlets.MetricsServlet;
import com.mycompany.myapp.web.filter.CachingHttpHeadersFilter;
import com.mycompany.myapp.web.filter.StaticResourcesProductionFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@ -19,6 +18,7 @@ import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import java.io.File;
import java.util.*;
import javax.inject.Inject;
import javax.servlet.*;
@ -35,7 +35,7 @@ public class WebConfigurer implements ServletContextInitializer, EmbeddedServlet
private Environment env;
@Inject
private JHipsterProperties props;
private JHipsterProperties jHipsterProperties;
@Autowired(required = false)
private MetricRegistry metricRegistry;
@ -44,12 +44,9 @@ public class WebConfigurer implements ServletContextInitializer, EmbeddedServlet
public void onStartup(ServletContext servletContext) throws ServletException {
log.info("Web application configuration, using profiles: {}", Arrays.toString(env.getActiveProfiles()));
EnumSet<DispatcherType> disps = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.ASYNC);
if (!env.acceptsProfiles(Constants.SPRING_PROFILE_FAST)) {
initMetrics(servletContext, disps);
}
initMetrics(servletContext, disps);
if (env.acceptsProfiles(Constants.SPRING_PROFILE_PRODUCTION)) {
initCachingHttpHeadersFilter(servletContext, disps);
initStaticResourcesProductionFilter(servletContext, disps);
}
if (env.acceptsProfiles(Constants.SPRING_PROFILE_DEVELOPMENT)) {
initH2Console(servletContext);
@ -58,7 +55,7 @@ public class WebConfigurer implements ServletContextInitializer, EmbeddedServlet
}
/**
* Set up Mime types.
* Set up Mime types and, if needed, set the document root.
*/
@Override
public void customize(ConfigurableEmbeddedServletContainer container) {
@ -68,24 +65,17 @@ public class WebConfigurer implements ServletContextInitializer, EmbeddedServlet
// CloudFoundry issue, see https://github.com/cloudfoundry/gorouter/issues/64
mappings.add("json", "text/html;charset=utf-8");
container.setMimeMappings(mappings);
}
/**
* Initializes the static resources production Filter.
*/
private void initStaticResourcesProductionFilter(ServletContext servletContext,
EnumSet<DispatcherType> disps) {
log.debug("Registering static resources production Filter");
FilterRegistration.Dynamic staticResourcesProductionFilter =
servletContext.addFilter("staticResourcesProductionFilter",
new StaticResourcesProductionFilter());
staticResourcesProductionFilter.addMappingForUrlPatterns(disps, true, "/");
staticResourcesProductionFilter.addMappingForUrlPatterns(disps, true, "/index.html");
staticResourcesProductionFilter.addMappingForUrlPatterns(disps, true, "/assets/*");
staticResourcesProductionFilter.addMappingForUrlPatterns(disps, true, "/scripts/*");
staticResourcesProductionFilter.setAsyncSupported(true);
// When running in an IDE or with ./mvnw spring-boot:run, set location of the static web assets.
File root;
if (env.acceptsProfiles(Constants.SPRING_PROFILE_PRODUCTION)) {
root = new File("target/www/");
} else {
root = new File("src/main/webapp/");
}
if (root.exists() && root.isDirectory()) {
container.setDocumentRoot(root);
}
}
/**
@ -96,10 +86,10 @@ public class WebConfigurer implements ServletContextInitializer, EmbeddedServlet
log.debug("Registering Caching HTTP Headers Filter");
FilterRegistration.Dynamic cachingHttpHeadersFilter =
servletContext.addFilter("cachingHttpHeadersFilter",
new CachingHttpHeadersFilter(env));
new CachingHttpHeadersFilter(jHipsterProperties));
cachingHttpHeadersFilter.addMappingForUrlPatterns(disps, true, "/dist/assets/*");
cachingHttpHeadersFilter.addMappingForUrlPatterns(disps, true, "/dist/scripts/*");
cachingHttpHeadersFilter.addMappingForUrlPatterns(disps, true, "/content/*");
cachingHttpHeadersFilter.addMappingForUrlPatterns(disps, true, "/app/*");
cachingHttpHeadersFilter.setAsyncSupported(true);
}
@ -132,7 +122,7 @@ public class WebConfigurer implements ServletContextInitializer, EmbeddedServlet
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = props.getCors();
CorsConfiguration config = jHipsterProperties.getCors();
if (config.getAllowedOrigins() != null && !config.getAllowedOrigins().isEmpty()) {
source.registerCorsConfiguration("/api/**", config);
source.registerCorsConfiguration("/v2/api-docs", config);
@ -148,7 +138,7 @@ public class WebConfigurer implements ServletContextInitializer, EmbeddedServlet
log.debug("Initialize H2 console");
ServletRegistration.Dynamic h2ConsoleServlet = servletContext.addServlet("H2Console", new org.h2.server.web.WebServlet());
h2ConsoleServlet.addMapping("/h2-console/*");
h2ConsoleServlet.setInitParameter("-properties", "src/main/resources");
h2ConsoleServlet.setInitParameter("-properties", "src/main/resources/");
h2ConsoleServlet.setLoadOnStartup(1);
}
}

View File

@ -6,11 +6,13 @@ import com.mycompany.myapp.config.JHipsterProperties;
import java.util.Date;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.context.annotation.*;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StopWatch;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@ -26,7 +28,7 @@ import static springfox.documentation.builders.PathSelectors.regex;
*/
@Configuration
@EnableSwagger2
@Profile("!" + Constants.SPRING_PROFILE_PRODUCTION)
@ConditionalOnExpression("#{!environment.acceptsProfiles('" + Constants.SPRING_PROFILE_NO_SWAGGER + "') && !environment.acceptsProfiles('" + Constants.SPRING_PROFILE_PRODUCTION + "')}")
public class SwaggerConfiguration {
private final Logger log = LoggerFactory.getLogger(SwaggerConfiguration.class);
@ -35,29 +37,36 @@ public class SwaggerConfiguration {
/**
* Swagger Springfox configuration.
*
* @param jHipsterProperties the properties of the application
* @return the Swagger Springfox configuration
*/
@Bean
@Profile("!" + Constants.SPRING_PROFILE_FAST)
public Docket swaggerSpringfoxDocket(JHipsterProperties jHipsterProperties) {
log.debug("Starting Swagger");
StopWatch watch = new StopWatch();
watch.start();
Contact contact = new Contact(
jHipsterProperties.getSwagger().getContactName(),
jHipsterProperties.getSwagger().getContactUrl(),
jHipsterProperties.getSwagger().getContactEmail());
ApiInfo apiInfo = new ApiInfo(
jHipsterProperties.getSwagger().getTitle(),
jHipsterProperties.getSwagger().getDescription(),
jHipsterProperties.getSwagger().getVersion(),
jHipsterProperties.getSwagger().getTermsOfServiceUrl(),
jHipsterProperties.getSwagger().getContact(),
contact,
jHipsterProperties.getSwagger().getLicense(),
jHipsterProperties.getSwagger().getLicenseUrl());
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo)
.genericModelSubstitutes(ResponseEntity.class)
.forCodeGeneration(true)
.genericModelSubstitutes(ResponseEntity.class)
.ignoredParameterTypes(Pageable.class)
.directModelSubstitute(java.time.LocalDate.class, String.class)
.ignoredParameterTypes(java.sql.Date.class)
.directModelSubstitute(java.time.LocalDate.class, java.sql.Date.class)
.directModelSubstitute(java.time.ZonedDateTime.class, Date.class)
.directModelSubstitute(java.time.LocalDateTime.class, Date.class)
.select()

View File

@ -16,7 +16,7 @@ import liquibase.integration.spring.SpringLiquibase;
/**
* Specific liquibase.integration.spring.SpringLiquibase that will update the database asynchronously.
* <p>
* By default, this asynchronous version only works when using the "dev" profile.<br/>
* By default, this asynchronous version only works when using the "dev" profile.<p>
* The standard liquibase.integration.spring.SpringLiquibase starts Liquibase in the current thread:
* <ul>
* <li>This is needed if you want to do some database requests at startup</li>
@ -27,7 +27,6 @@ import liquibase.integration.spring.SpringLiquibase;
* <li>On a recent MacBook Pro, start-up time is down from 14 seconds to 8 seconds</li>
* <li>In production, this can help your application run on platforms like Heroku, where it must start/restart very quickly</li>
* </ul>
* </p>
*/
public class AsyncSpringLiquibase extends SpringLiquibase {
@ -42,18 +41,22 @@ public class AsyncSpringLiquibase extends SpringLiquibase {
@Override
public void afterPropertiesSet() throws LiquibaseException {
if (env.acceptsProfiles(Constants.SPRING_PROFILE_DEVELOPMENT, Constants.SPRING_PROFILE_HEROKU)) {
taskExecutor.execute(() -> {
try {
log.warn("Starting Liquibase asynchronously, your database might not be ready at startup!");
initDb();
} catch (LiquibaseException e) {
log.error("Liquibase could not start correctly, your database is NOT ready: {}", e.getMessage(), e);
}
});
if (!env.acceptsProfiles(Constants.SPRING_PROFILE_NO_LIQUIBASE)) {
if (env.acceptsProfiles(Constants.SPRING_PROFILE_DEVELOPMENT, Constants.SPRING_PROFILE_HEROKU)) {
taskExecutor.execute(() -> {
try {
log.warn("Starting Liquibase asynchronously, your database might not be ready at startup!");
initDb();
} catch (LiquibaseException e) {
log.error("Liquibase could not start correctly, your database is NOT ready: {}", e.getMessage(), e);
}
});
} else {
log.debug("Starting Liquibase synchronously");
initDb();
}
} else {
log.debug("Starting Liquibase synchronously");
initDb();
log.debug("Liquibase is disabled");
}
}

View File

@ -28,13 +28,11 @@ public abstract class AbstractAuditingEntity implements Serializable {
private static final long serialVersionUID = 1L;
@CreatedBy
@NotNull
@Column(name = "created_by", nullable = false, length = 50, updatable = false)
@JsonIgnore
private String createdBy;
@CreatedDate
@NotNull
@Column(name = "created_date", nullable = false)
@JsonIgnore
private ZonedDateTime createdDate = ZonedDateTime.now();

View File

@ -18,6 +18,8 @@ import java.io.Serializable;
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Authority implements Serializable {
private static final long serialVersionUID = 1L;
@NotNull
@Size(min = 0, max = 50)
@Id

View File

@ -20,6 +20,8 @@ import java.util.Objects;
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class BankAccount implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@ -27,13 +29,12 @@ public class BankAccount implements Serializable {
@NotNull
@Column(name = "name", nullable = false)
private String name;
@NotNull
@Column(name = "balance", precision=10, scale=2, nullable = false)
private BigDecimal balance;
@ManyToOne
@JoinColumn(name = "user_id")
private User user;
@OneToMany(mappedBy = "bankAccount")
@ -52,7 +53,7 @@ public class BankAccount implements Serializable {
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@ -60,7 +61,7 @@ public class BankAccount implements Serializable {
public BigDecimal getBalance() {
return balance;
}
public void setBalance(BigDecimal balance) {
this.balance = balance;
}

View File

@ -19,6 +19,8 @@ import java.util.Objects;
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Label implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@ -27,7 +29,7 @@ public class Label implements Serializable {
@Size(min = 3)
@Column(name = "label", nullable = false)
private String label;
@ManyToMany(mappedBy = "labels")
@JsonIgnore
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@ -44,7 +46,7 @@ public class Label implements Serializable {
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}

View File

@ -2,12 +2,12 @@ package com.mycompany.myapp.domain;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import java.time.ZonedDateTime;
import javax.persistence.*;
import javax.validation.constraints.*;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.ZonedDateTime;
import java.util.HashSet;
import java.util.Set;
import java.util.Objects;
@ -20,6 +20,8 @@ import java.util.Objects;
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Operation implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@ -27,16 +29,15 @@ public class Operation implements Serializable {
@NotNull
@Column(name = "date", nullable = false)
private ZonedDateTime date;
@Column(name = "description")
private String description;
@NotNull
@Column(name = "amount", precision=10, scale=2, nullable = false)
private BigDecimal amount;
@ManyToOne
@JoinColumn(name = "bank_account_id")
private BankAccount bankAccount;
@ManyToMany
@ -57,7 +58,7 @@ public class Operation implements Serializable {
public ZonedDateTime getDate() {
return date;
}
public void setDate(ZonedDateTime date) {
this.date = date;
}
@ -65,7 +66,7 @@ public class Operation implements Serializable {
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@ -73,7 +74,7 @@ public class Operation implements Serializable {
public BigDecimal getAmount() {
return amount;
}
public void setAmount(BigDecimal amount) {
this.amount = amount;
}

View File

@ -22,10 +22,9 @@ import java.io.Serializable;
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class PersistentToken implements Serializable {
private static final long serialVersionUID = 1L;
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("d MMMM yyyy");
private static final int MAX_USER_AGENT_LEN = 255;

View File

@ -22,6 +22,8 @@ import java.time.ZonedDateTime;
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class User extends AbstractAuditingEntity implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@ -46,11 +48,13 @@ public class User extends AbstractAuditingEntity implements Serializable {
@Column(name = "last_name", length = 50)
private String lastName;
@NotNull
@Email
@Size(max = 100)
@Column(length = 100, unique = true)
private String email;
@NotNull
@Column(nullable = false)
private boolean activated = false;

View File

@ -12,7 +12,7 @@ import com.fasterxml.jackson.databind.SerializerProvider;
public final class JSR310DateTimeSerializer extends JsonSerializer<TemporalAccessor> {
private static final DateTimeFormatter ISOFormatter =
DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(ZoneId.of("Z"));
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").withZone(ZoneId.of("Z"));
public static final JSR310DateTimeSerializer INSTANCE = new JSR310DateTimeSerializer();

View File

@ -5,7 +5,6 @@ import com.mycompany.myapp.domain.PersistentAuditEvent;
import org.springframework.boot.actuate.audit.AuditEvent;
import org.springframework.boot.actuate.audit.AuditEventRepository;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@ -18,54 +17,48 @@ import java.util.Date;
import java.util.List;
/**
* Wraps an implementation of Spring Boot's AuditEventRepository.
* An implementation of Spring Boot's AuditEventRepository.
*/
@Repository
public class CustomAuditEventRepository {
public class CustomAuditEventRepository implements AuditEventRepository {
private static final String AUTHORIZATION_FAILURE = "AUTHORIZATION_FAILURE";
private static final String ANONYMOUS_USER = "anonymousUser";
@Inject
private PersistenceAuditEventRepository persistenceAuditEventRepository;
@Bean
public AuditEventRepository auditEventRepository() {
return new AuditEventRepository() {
@Inject
private AuditEventConverter auditEventConverter;
private static final String AUTHORIZATION_FAILURE = "AUTHORIZATION_FAILURE";
@Override
public List<AuditEvent> find(String principal, Date after) {
Iterable<PersistentAuditEvent> persistentAuditEvents;
if (principal == null && after == null) {
persistentAuditEvents = persistenceAuditEventRepository.findAll();
} else if (after == null) {
persistentAuditEvents = persistenceAuditEventRepository.findByPrincipal(principal);
} else {
persistentAuditEvents =
persistenceAuditEventRepository.findByPrincipalAndAuditEventDateAfter(principal, LocalDateTime.from(after.toInstant()));
}
return auditEventConverter.convertToAuditEvent(persistentAuditEvents);
}
private static final String ANONYMOUS_USER = "anonymousUser";
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void add(AuditEvent event) {
if (!AUTHORIZATION_FAILURE.equals(event.getType()) &&
!ANONYMOUS_USER.equals(event.getPrincipal().toString())) {
@Inject
private AuditEventConverter auditEventConverter;
@Override
public List<AuditEvent> find(String principal, Date after) {
Iterable<PersistentAuditEvent> persistentAuditEvents;
if (principal == null && after == null) {
persistentAuditEvents = persistenceAuditEventRepository.findAll();
} else if (after == null) {
persistentAuditEvents = persistenceAuditEventRepository.findByPrincipal(principal);
} else {
persistentAuditEvents =
persistenceAuditEventRepository.findByPrincipalAndAuditEventDateAfter(principal, LocalDateTime.from(after.toInstant()));
}
return auditEventConverter.convertToAuditEvent(persistentAuditEvents);
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void add(AuditEvent event) {
if (!AUTHORIZATION_FAILURE.equals(event.getType()) &&
!ANONYMOUS_USER.equals(event.getPrincipal().toString())) {
PersistentAuditEvent persistentAuditEvent = new PersistentAuditEvent();
persistentAuditEvent.setPrincipal(event.getPrincipal());
persistentAuditEvent.setAuditEventType(event.getType());
Instant instant = Instant.ofEpochMilli(event.getTimestamp().getTime());
persistentAuditEvent.setAuditEventDate(LocalDateTime.ofInstant(instant, ZoneId.systemDefault()));
persistentAuditEvent.setData(auditEventConverter.convertDataToStrings(event.getData()));
persistenceAuditEventRepository.save(persistentAuditEvent);
}
}
};
PersistentAuditEvent persistentAuditEvent = new PersistentAuditEvent();
persistentAuditEvent.setPrincipal(event.getPrincipal());
persistentAuditEvent.setAuditEventType(event.getType());
Instant instant = Instant.ofEpochMilli(event.getTimestamp().getTime());
persistentAuditEvent.setAuditEventDate(LocalDateTime.ofInstant(instant, ZoneId.systemDefault()));
persistentAuditEvent.setData(auditEventConverter.convertDataToStrings(event.getData()));
persistenceAuditEventRepository.save(persistentAuditEvent);
}
}
}

View File

@ -4,6 +4,8 @@ import com.mycompany.myapp.domain.PersistentAuditEvent;
import java.time.LocalDateTime;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import java.util.List;
@ -16,5 +18,5 @@ public interface PersistenceAuditEventRepository extends JpaRepository<Persisten
List<PersistentAuditEvent> findByPrincipalAndAuditEventDateAfter(String principal, LocalDateTime after);
List<PersistentAuditEvent> findAllByAuditEventDateBetween(LocalDateTime fromDate, LocalDateTime toDate);
Page<PersistentAuditEvent> findAllByAuditEventDateBetween(LocalDateTime fromDate, LocalDateTime toDate, Pageable pageable);
}

View File

@ -4,6 +4,7 @@ import com.mycompany.myapp.domain.PersistentToken;
import com.mycompany.myapp.domain.User;
import com.mycompany.myapp.repository.PersistentTokenRepository;
import com.mycompany.myapp.repository.UserRepository;
import com.mycompany.myapp.config.JHipsterProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.Environment;
@ -25,9 +26,9 @@ import java.util.Arrays;
/**
* Custom implementation of Spring Security's RememberMeServices.
* <p/>
* <p>
* Persistent tokens are used by Spring Security to automatically log in users.
* <p/>
* <p>
* This is a specific implementation of Spring Security's remember-me authentication, but it is much
* more powerful than the standard implementations:
* <ul>
@ -35,17 +36,16 @@ import java.util.Arrays;
* <li>It stores more information, such as the IP address and the user agent, for audit purposes<li>
* <li>When a user logs out, only his current session is invalidated, and not all of his sessions</li>
* </ul>
* <p/>
* <p>
* This is inspired by:
* <ul>
* <li><a href="http://jaspan.com/improved_persistent_login_cookie_best_practice">Improved Persistent Login Cookie
* Best Practice</a></li>
* <li><a href="https://github.com/blog/1661-modeling-your-app-s-user-session">Github's "Modeling your App's User Session"</a></li></li>
* <li><a href="https://github.com/blog/1661-modeling-your-app-s-user-session">Github's "Modeling your App's User Session"</a></li>
* </ul>
* <p/>
* <p>
* The main algorithm comes from Spring Security's PersistentTokenBasedRememberMeServices, but this class
* couldn't be cleanly extended.
* <p/>
*/
@Service
public class CustomPersistentRememberMeServices extends
@ -71,15 +71,14 @@ public class CustomPersistentRememberMeServices extends
private UserRepository userRepository;
@Inject
public CustomPersistentRememberMeServices(Environment env, org.springframework.security.core.userdetails
public CustomPersistentRememberMeServices(JHipsterProperties jHipsterProperties, org.springframework.security.core.userdetails
.UserDetailsService userDetailsService) {
super(env.getProperty("jhipster.security.rememberme.key"), userDetailsService);
super(jHipsterProperties.getSecurity().getRememberMe().getKey(), userDetailsService);
random = new SecureRandom();
}
@Override
@Transactional
protected UserDetails processAutoLoginCookie(String[] cookieTokens, HttpServletRequest request,
HttpServletResponse response) {
@ -129,7 +128,7 @@ public class CustomPersistentRememberMeServices extends
/**
* When logout occurs, only invalidate the current token, and not all user sessions.
* <p/>
* <p>
* The standard Spring Security implementations are too basic: they invalidate all tokens for the
* current user, so when he logs out from one browser, all his other sessions are destroyed.
*/

View File

@ -5,7 +5,6 @@ import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
@ -20,6 +19,8 @@ public final class SecurityUtils {
/**
* Get the login of the current user.
*
* @return the login of the current user
*/
public static String getCurrentUserLogin() {
SecurityContext securityContext = SecurityContextHolder.getContext();
@ -54,27 +55,13 @@ public final class SecurityUtils {
return true;
}
/**
* Return the current user, or throws an exception, if the user is not
* authenticated yet.
*
* @return the current user
*/
public static User getCurrentUser() {
SecurityContext securityContext = SecurityContextHolder.getContext();
Authentication authentication = securityContext.getAuthentication();
if (authentication != null) {
if (authentication.getPrincipal() instanceof User) {
return (User) authentication.getPrincipal();
}
}
throw new IllegalStateException("User not found!");
}
/**
* If the current user has a specific authority (security role).
*
* <p>The name of this method comes from the isUserInRole() method in the Servlet API</p>
*
* @param authority the authorithy to check
* @return true if the current user has the authority, false otherwise
*/
public static boolean isCurrentUserInRole(String authority) {
SecurityContext securityContext = SecurityContextHolder.getContext();

View File

@ -1,10 +1,11 @@
package com.mycompany.myapp.service;
import com.mycompany.myapp.config.audit.AuditEventConverter;
import com.mycompany.myapp.domain.PersistentAuditEvent;
import com.mycompany.myapp.repository.PersistenceAuditEventRepository;
import java.time.LocalDateTime;
import org.springframework.boot.actuate.audit.AuditEvent;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -14,7 +15,6 @@ import java.util.Optional;
/**
* Service for managing audit events.
* <p/>
* <p>
* This is the default implementation to support SpringBoot Actuator AuditEventRepository
* </p>
@ -36,15 +36,14 @@ public class AuditEventService {
this.auditEventConverter = auditEventConverter;
}
public List<AuditEvent> findAll() {
return auditEventConverter.convertToAuditEvent(persistenceAuditEventRepository.findAll());
public Page<AuditEvent> findAll(Pageable pageable) {
return persistenceAuditEventRepository.findAll(pageable)
.map(persistentAuditEvents -> auditEventConverter.convertToAuditEvent(persistentAuditEvents));
}
public List<AuditEvent> findByDates(LocalDateTime fromDate, LocalDateTime toDate) {
List<PersistentAuditEvent> persistentAuditEvents =
persistenceAuditEventRepository.findAllByAuditEventDateBetween(fromDate, toDate);
return auditEventConverter.convertToAuditEvent(persistentAuditEvents);
public Page<AuditEvent> findByDates(LocalDateTime fromDate, LocalDateTime toDate, Pageable pageable) {
return persistenceAuditEventRepository.findAllByAuditEventDateBetween(fromDate, toDate, pageable)
.map(persistentAuditEvents -> auditEventConverter.convertToAuditEvent(persistentAuditEvents));
}
public Optional<AuditEvent> find(Long id) {

View File

@ -22,7 +22,6 @@ import java.util.Locale;
/**
* Service for sending e-mails.
* <p/>
* <p>
* We use the @Async annotation to send e-mails asynchronously.
* </p>

View File

@ -31,12 +31,14 @@ public class UserService {
private final Logger log = LoggerFactory.getLogger(UserService.class);
@Inject
private PasswordEncoder passwordEncoder;
@Inject
private UserRepository userRepository;
@Inject
private PersistentTokenRepository persistentTokenRepository;
@ -45,7 +47,7 @@ public class UserService {
public Optional<User> activateRegistration(String key) {
log.debug("Activating user for activation key {}", key);
userRepository.findOneByActivationKey(key)
return userRepository.findOneByActivationKey(key)
.map(user -> {
// activate given user for the registration key.
user.setActivated(true);
@ -54,7 +56,6 @@ public class UserService {
log.debug("Activated user: {}", user);
return user;
});
return Optional.empty();
}
public Optional<User> completePasswordReset(String newPassword, String key) {
@ -117,7 +118,7 @@ public class UserService {
user.setLastName(managedUserDTO.getLastName());
user.setEmail(managedUserDTO.getEmail());
if (managedUserDTO.getLangKey() == null) {
user.setLangKey("en"); // default language is English
user.setLangKey("en"); // default language
} else {
user.setLangKey(managedUserDTO.getLangKey());
}
@ -139,7 +140,7 @@ public class UserService {
}
public void updateUserInformation(String firstName, String lastName, String email, String langKey) {
userRepository.findOneByLogin(SecurityUtils.getCurrentUser().getUsername()).ifPresent(u -> {
userRepository.findOneByLogin(SecurityUtils.getCurrentUserLogin()).ifPresent(u -> {
u.setFirstName(firstName);
u.setLastName(lastName);
u.setEmail(email);
@ -157,7 +158,7 @@ public class UserService {
}
public void changePassword(String password) {
userRepository.findOneByLogin(SecurityUtils.getCurrentUser().getUsername()).ifPresent(u -> {
userRepository.findOneByLogin(SecurityUtils.getCurrentUserLogin()).ifPresent(u -> {
String encryptedPassword = passwordEncoder.encode(password);
u.setPassword(encryptedPassword);
userRepository.save(u);
@ -182,7 +183,7 @@ public class UserService {
@Transactional(readOnly = true)
public User getUserWithAuthorities() {
User user = userRepository.findOneByLogin(SecurityUtils.getCurrentUser().getUsername()).get();
User user = userRepository.findOneByLogin(SecurityUtils.getCurrentUserLogin()).get();
user.getAuthorities().size(); // eagerly load the association
return user;
}
@ -190,7 +191,6 @@ public class UserService {
/**
* Persistent Token are used for providing automatic authentication, they should be automatically deleted after
* 30 days.
* <p/>
* <p>
* This is scheduled to get fired everyday, at midnight.
* </p>
@ -208,7 +208,6 @@ public class UserService {
/**
* Not activated users should be automatically deleted after 3 days.
* <p/>
* <p>
* This is scheduled to get fired everyday, at 01:00 (am).
* </p>

View File

@ -1,6 +1,6 @@
package com.mycompany.myapp.web.filter;
import org.springframework.core.env.Environment;
import com.mycompany.myapp.config.JHipsterProperties;
import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
@ -15,18 +15,17 @@ public class CachingHttpHeadersFilter implements Filter {
// We consider the last modified date is the start up time of the server
private final static long LAST_MODIFIED = System.currentTimeMillis();
private long CACHE_TIME_TO_LIVE = TimeUnit.DAYS.toMillis(31L);
private long CACHE_TIME_TO_LIVE = TimeUnit.DAYS.toMillis(1461L);
private Environment env;
private JHipsterProperties jHipsterProperties;;
public CachingHttpHeadersFilter(Environment env) {
this.env = env;
public CachingHttpHeadersFilter(JHipsterProperties jHipsterProperties) {
this.jHipsterProperties = jHipsterProperties;
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
CACHE_TIME_TO_LIVE = TimeUnit.DAYS.toMillis(env.getProperty("jhipster.http.cache.timeToLiveInDays",
Long.class, 31L));
CACHE_TIME_TO_LIVE = TimeUnit.DAYS.toMillis(jHipsterProperties.getHttp().getCache().getTimeToLiveInDays());
}
@Override

View File

@ -1,42 +0,0 @@
package com.mycompany.myapp.web.filter;
import org.apache.commons.lang.StringUtils;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* This filter is used in production, to serve static resources generated by "grunt build".
* <p/>
* <p>
* It is configured to serve resources from the "dist" directory, which is the Grunt
* destination directory.
* </p>
*/
public class StaticResourcesProductionFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// Nothing to initialize
}
@Override
public void destroy() {
// Nothing to destroy
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String contextPath = ((HttpServletRequest) request).getContextPath();
String requestURI = httpRequest.getRequestURI();
requestURI = StringUtils.substringAfter(requestURI, contextPath);
if (StringUtils.equals("/", requestURI)) {
requestURI = "/index.html";
}
String newURI = "/dist" + requestURI;
request.getRequestDispatcher(newURI).forward(request, response);
}
}

View File

@ -16,6 +16,7 @@ import com.mycompany.myapp.web.rest.util.HeaderUtil;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
@ -50,17 +51,25 @@ public class AccountResource {
private MailService mailService;
/**
* POST /register -> register the user.
* POST /register : register the user.
*
* @param userDTO the user DTO
* @param request the HTTP request
* @return the ResponseEntity with status 201 (Created) if the user is registred or 400 (Bad Request) if the login or e-mail is already in use
*/
@RequestMapping(value = "/register",
method = RequestMethod.POST,
produces = MediaType.TEXT_PLAIN_VALUE)
method = RequestMethod.POST,
produces={MediaType.APPLICATION_JSON_VALUE, MediaType.TEXT_PLAIN_VALUE})
@Timed
public ResponseEntity<?> registerAccount(@Valid @RequestBody UserDTO userDTO, HttpServletRequest request) {
HttpHeaders textPlainHeaders = new HttpHeaders();
textPlainHeaders.setContentType(MediaType.TEXT_PLAIN);
return userRepository.findOneByLogin(userDTO.getLogin())
.map(user -> new ResponseEntity<>("login already in use", HttpStatus.BAD_REQUEST))
.map(user -> new ResponseEntity<>("login already in use", textPlainHeaders, HttpStatus.BAD_REQUEST))
.orElseGet(() -> userRepository.findOneByEmail(userDTO.getEmail())
.map(user -> new ResponseEntity<>("e-mail address already in use", HttpStatus.BAD_REQUEST))
.map(user -> new ResponseEntity<>("e-mail address already in use", textPlainHeaders, HttpStatus.BAD_REQUEST))
.orElseGet(() -> {
User user = userService.createUserInformation(userDTO.getLogin(), userDTO.getPassword(),
userDTO.getFirstName(), userDTO.getLastName(), userDTO.getEmail().toLowerCase(),
@ -79,20 +88,26 @@ public class AccountResource {
}
/**
* GET /activate -> activate the registered user.
* GET /activate : activate the registered user.
*
* @param key the activation key
* @return the ResponseEntity with status 200 (OK) and the activated user in body, or status 500 (Internal Server Error) if the user couldn't be activated
*/
@RequestMapping(value = "/activate",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
@Timed
public ResponseEntity<String> activateAccount(@RequestParam(value = "key") String key) {
return Optional.ofNullable(userService.activateRegistration(key))
return userService.activateRegistration(key)
.map(user -> new ResponseEntity<String>(HttpStatus.OK))
.orElse(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
}
/**
* GET /authenticate -> check if the user is authenticated, and return its login.
* GET /authenticate : check if the user is authenticated, and return its login.
*
* @param request the HTTP request
* @return the login if the user is authenticated
*/
@RequestMapping(value = "/authenticate",
method = RequestMethod.GET,
@ -104,7 +119,9 @@ public class AccountResource {
}
/**
* GET /account -> get the current user.
* GET /account : get the current user.
*
* @return the ResponseEntity with status 200 (OK) and the current user in body, or status 500 (Internal Server Error) if the user couldn't be returned
*/
@RequestMapping(value = "/account",
method = RequestMethod.GET,
@ -117,7 +134,10 @@ public class AccountResource {
}
/**
* POST /account -> update the current user information.
* POST /account : update the current user information.
*
* @param userDTO the current user information
* @return the ResponseEntity with status 200 (OK), or status 400 (Bad Request) or 500 (Internal Server Error) if the user couldn't be updated
*/
@RequestMapping(value = "/account",
method = RequestMethod.POST,
@ -129,7 +149,7 @@ public class AccountResource {
return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert("user-management", "emailexists", "Email already in use")).body(null);
}
return userRepository
.findOneByLogin(SecurityUtils.getCurrentUser().getUsername())
.findOneByLogin(SecurityUtils.getCurrentUserLogin())
.map(u -> {
userService.updateUserInformation(userDTO.getFirstName(), userDTO.getLastName(), userDTO.getEmail(),
userDTO.getLangKey());
@ -139,11 +159,14 @@ public class AccountResource {
}
/**
* POST /change_password -> changes the current user's password
* POST /account/change_password : changes the current user's password
*
* @param password the new password
* @return the ResponseEntity with status 200 (OK), or status 400 (Bad Request) if the new password is not strong enough
*/
@RequestMapping(value = "/account/change_password",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
produces = MediaType.TEXT_PLAIN_VALUE)
@Timed
public ResponseEntity<?> changePassword(@RequestBody String password) {
if (!checkPasswordLength(password)) {
@ -154,14 +177,17 @@ public class AccountResource {
}
/**
* GET /account/sessions -> get the current open sessions.
* GET /account/sessions : get the current open sessions.
*
* @return the ResponseEntity with status 200 (OK) and the current open sessions in body,
* or status 500 (Internal Server Error) if the current open sessions couldn't be retrieved
*/
@RequestMapping(value = "/account/sessions",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
@Timed
public ResponseEntity<List<PersistentToken>> getCurrentSessions() {
return userRepository.findOneByLogin(SecurityUtils.getCurrentUser().getUsername())
return userRepository.findOneByLogin(SecurityUtils.getCurrentUserLogin())
.map(user -> new ResponseEntity<>(
persistentTokenRepository.findByUser(user),
HttpStatus.OK))
@ -169,7 +195,7 @@ public class AccountResource {
}
/**
* DELETE /account/sessions?series={series} -> invalidate an existing session.
* DELETE /account/sessions?series={series} : invalidate an existing session.
*
* - You can only delete your own sessions, not any other user's session
* - If you delete one of your existing sessions, and that you are currently logged in on that session, you will
@ -180,19 +206,29 @@ public class AccountResource {
* anymore.
* There is an API to invalidate the current session, but there is no API to check which session uses which
* cookie.
*
* @param series the series of an existing session
* @throws UnsupportedEncodingException if the series couldnt be URL decoded
*/
@RequestMapping(value = "/account/sessions/{series}",
method = RequestMethod.DELETE)
@Timed
public void invalidateSession(@PathVariable String series) throws UnsupportedEncodingException {
String decodedSeries = URLDecoder.decode(series, "UTF-8");
userRepository.findOneByLogin(SecurityUtils.getCurrentUser().getUsername()).ifPresent(u -> {
userRepository.findOneByLogin(SecurityUtils.getCurrentUserLogin()).ifPresent(u -> {
persistentTokenRepository.findByUser(u).stream()
.filter(persistentToken -> StringUtils.equals(persistentToken.getSeries(), decodedSeries))
.findAny().ifPresent(t -> persistentTokenRepository.delete(decodedSeries));
});
}
/**
* POST /account/reset_password/init : Send an e-mail to reset the password of the user
*
* @param mail the mail of the user
* @param request the HTTP request
* @return the ResponseEntity with status 200 (OK) if the e-mail was sent, or status 400 (Bad Request) if the e-mail address is not registred
*/
@RequestMapping(value = "/account/reset_password/init",
method = RequestMethod.POST,
produces = MediaType.TEXT_PLAIN_VALUE)
@ -211,16 +247,24 @@ public class AccountResource {
}).orElse(new ResponseEntity<>("e-mail address not registered", HttpStatus.BAD_REQUEST));
}
/**
* POST /account/reset_password/finish : Finish to reset the password of the user
*
* @param keyAndPassword the generated key and the new password
* @return the ResponseEntity with status 200 (OK) if the password has been reset,
* or status 400 (Bad Request) or 500 (Internal Server Error) if the password could not be reset
*/
@RequestMapping(value = "/account/reset_password/finish",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
produces = MediaType.TEXT_PLAIN_VALUE)
@Timed
public ResponseEntity<String> finishPasswordReset(@RequestBody KeyAndPasswordDTO keyAndPassword) {
if (!checkPasswordLength(keyAndPassword.getNewPassword())) {
return new ResponseEntity<>("Incorrect password", HttpStatus.BAD_REQUEST);
}
return userService.completePasswordReset(keyAndPassword.getNewPassword(), keyAndPassword.getKey())
.map(user -> new ResponseEntity<String>(HttpStatus.OK)).orElse(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
.map(user -> new ResponseEntity<String>(HttpStatus.OK))
.orElse(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
}
private boolean checkPasswordLength(String password) {

View File

@ -3,13 +3,18 @@ package com.mycompany.myapp.web.rest;
import com.mycompany.myapp.service.AuditEventService;
import java.time.LocalDate;
import com.mycompany.myapp.web.rest.util.PaginationUtil;
import org.springframework.boot.actuate.audit.AuditEvent;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import java.net.URISyntaxException;
import javax.inject.Inject;
import java.util.List;
@ -27,20 +32,48 @@ public class AuditResource {
this.auditEventService = auditEventService;
}
/**
* GET /audits : get a page of AuditEvents.
*
* @param pageable the pagination information
* @return the ResponseEntity with status 200 (OK) and the list of AuditEvents in body
* @throws URISyntaxException if there is an error to generate the pagination HTTP headers
*/
@RequestMapping(method = RequestMethod.GET)
public List<AuditEvent> getAll() {
return auditEventService.findAll();
public ResponseEntity<List<AuditEvent>> getAll(Pageable pageable) throws URISyntaxException {
Page<AuditEvent> page = auditEventService.findAll(pageable);
HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/audits");
return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK);
}
/**
* GET /audits : get a page of AuditEvents between the fromDate and toDate.
*
* @param fromDate the start of the time period of AuditEvents to get
* @param toDate the end of the time period of AuditEvents to get
* @param pageable the pagination information
* @return the ResponseEntity with status 200 (OK) and the list of AuditEvents in body
* @throws URISyntaxException if there is an error to generate the pagination HTTP headers
*/
@RequestMapping(method = RequestMethod.GET,
params = {"fromDate", "toDate"})
public List<AuditEvent> getByDates(
public ResponseEntity<List<AuditEvent>> getByDates(
@RequestParam(value = "fromDate") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate fromDate,
@RequestParam(value = "toDate") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate toDate) {
@RequestParam(value = "toDate") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate toDate,
Pageable pageable) throws URISyntaxException {
return auditEventService.findByDates(fromDate.atTime(0, 0), toDate.atTime(23, 59));
Page<AuditEvent> page = auditEventService.findByDates(fromDate.atTime(0, 0), toDate.atTime(23, 59), pageable);
HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/audits");
return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK);
}
/**
* GET /audits/:id : get an AuditEvent by id.
*
* @param id the id of the entity to get
* @return the ResponseEntity with status 200 (OK) and the AuditEvent in body, or status 404 (Not Found)
*/
@RequestMapping(value = "/{id:.+}",
method = RequestMethod.GET)
public ResponseEntity<AuditEvent> get(@PathVariable Long id) {

View File

@ -32,9 +32,13 @@ public class BankAccountResource {
private BankAccountRepository bankAccountRepository;
/**
* POST /bankAccounts -> Create a new bankAccount.
* POST /bank-accounts : Create a new bankAccount.
*
* @param bankAccount the bankAccount to create
* @return the ResponseEntity with status 201 (Created) and with body the new bankAccount, or with status 400 (Bad Request) if the bankAccount has already an ID
* @throws URISyntaxException if the Location URI syntax is incorrect
*/
@RequestMapping(value = "/bankAccounts",
@RequestMapping(value = "/bank-accounts",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
@Timed
@ -44,15 +48,21 @@ public class BankAccountResource {
return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert("bankAccount", "idexists", "A new bankAccount cannot already have an ID")).body(null);
}
BankAccount result = bankAccountRepository.save(bankAccount);
return ResponseEntity.created(new URI("/api/bankAccounts/" + result.getId()))
return ResponseEntity.created(new URI("/api/bank-accounts/" + result.getId()))
.headers(HeaderUtil.createEntityCreationAlert("bankAccount", result.getId().toString()))
.body(result);
}
/**
* PUT /bankAccounts -> Updates an existing bankAccount.
* PUT /bank-accounts : Updates an existing bankAccount.
*
* @param bankAccount the bankAccount to update
* @return the ResponseEntity with status 200 (OK) and with body the updated bankAccount,
* or with status 400 (Bad Request) if the bankAccount is not valid,
* or with status 500 (Internal Server Error) if the bankAccount couldnt be updated
* @throws URISyntaxException if the Location URI syntax is incorrect
*/
@RequestMapping(value = "/bankAccounts",
@RequestMapping(value = "/bank-accounts",
method = RequestMethod.PUT,
produces = MediaType.APPLICATION_JSON_VALUE)
@Timed
@ -68,21 +78,27 @@ public class BankAccountResource {
}
/**
* GET /bankAccounts -> get all the bankAccounts.
* GET /bank-accounts : get all the bankAccounts.
*
* @return the ResponseEntity with status 200 (OK) and the list of bankAccounts in body
*/
@RequestMapping(value = "/bankAccounts",
@RequestMapping(value = "/bank-accounts",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
@Timed
public List<BankAccount> getAllBankAccounts() {
log.debug("REST request to get all BankAccounts");
return bankAccountRepository.findAll();
}
List<BankAccount> bankAccounts = bankAccountRepository.findAll();
return bankAccounts;
}
/**
* GET /bankAccounts/:id -> get the "id" bankAccount.
* GET /bank-accounts/:id : get the "id" bankAccount.
*
* @param id the id of the bankAccount to retrieve
* @return the ResponseEntity with status 200 (OK) and with body the bankAccount, or with status 404 (Not Found)
*/
@RequestMapping(value = "/bankAccounts/{id}",
@RequestMapping(value = "/bank-accounts/{id}",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
@Timed
@ -97,9 +113,12 @@ public class BankAccountResource {
}
/**
* DELETE /bankAccounts/:id -> delete the "id" bankAccount.
* DELETE /bank-accounts/:id : delete the "id" bankAccount.
*
* @param id the id of the bankAccount to delete
* @return the ResponseEntity with status 200 (OK)
*/
@RequestMapping(value = "/bankAccounts/{id}",
@RequestMapping(value = "/bank-accounts/{id}",
method = RequestMethod.DELETE,
produces = MediaType.APPLICATION_JSON_VALUE)
@Timed
@ -108,4 +127,5 @@ public class BankAccountResource {
bankAccountRepository.delete(id);
return ResponseEntity.ok().headers(HeaderUtil.createEntityDeletionAlert("bankAccount", id.toString())).build();
}
}

View File

@ -32,7 +32,11 @@ public class LabelResource {
private LabelRepository labelRepository;
/**
* POST /labels -> Create a new label.
* POST /labels : Create a new label.
*
* @param label the label to create
* @return the ResponseEntity with status 201 (Created) and with body the new label, or with status 400 (Bad Request) if the label has already an ID
* @throws URISyntaxException if the Location URI syntax is incorrect
*/
@RequestMapping(value = "/labels",
method = RequestMethod.POST,
@ -50,7 +54,13 @@ public class LabelResource {
}
/**
* PUT /labels -> Updates an existing label.
* PUT /labels : Updates an existing label.
*
* @param label the label to update
* @return the ResponseEntity with status 200 (OK) and with body the updated label,
* or with status 400 (Bad Request) if the label is not valid,
* or with status 500 (Internal Server Error) if the label couldnt be updated
* @throws URISyntaxException if the Location URI syntax is incorrect
*/
@RequestMapping(value = "/labels",
method = RequestMethod.PUT,
@ -68,7 +78,9 @@ public class LabelResource {
}
/**
* GET /labels -> get all the labels.
* GET /labels : get all the labels.
*
* @return the ResponseEntity with status 200 (OK) and the list of labels in body
*/
@RequestMapping(value = "/labels",
method = RequestMethod.GET,
@ -76,11 +88,15 @@ public class LabelResource {
@Timed
public List<Label> getAllLabels() {
log.debug("REST request to get all Labels");
return labelRepository.findAll();
}
List<Label> labels = labelRepository.findAll();
return labels;
}
/**
* GET /labels/:id -> get the "id" label.
* GET /labels/:id : get the "id" label.
*
* @param id the id of the label to retrieve
* @return the ResponseEntity with status 200 (OK) and with body the label, or with status 404 (Not Found)
*/
@RequestMapping(value = "/labels/{id}",
method = RequestMethod.GET,
@ -97,7 +113,10 @@ public class LabelResource {
}
/**
* DELETE /labels/:id -> delete the "id" label.
* DELETE /labels/:id : delete the "id" label.
*
* @param id the id of the label to delete
* @return the ResponseEntity with status 200 (OK)
*/
@RequestMapping(value = "/labels/{id}",
method = RequestMethod.DELETE,
@ -108,4 +127,5 @@ public class LabelResource {
labelRepository.delete(id);
return ResponseEntity.ok().headers(HeaderUtil.createEntityDeletionAlert("label", id.toString())).build();
}
}

View File

@ -35,7 +35,11 @@ public class OperationResource {
private OperationRepository operationRepository;
/**
* POST /operations -> Create a new operation.
* POST /operations : Create a new operation.
*
* @param operation the operation to create
* @return the ResponseEntity with status 201 (Created) and with body the new operation, or with status 400 (Bad Request) if the operation has already an ID
* @throws URISyntaxException if the Location URI syntax is incorrect
*/
@RequestMapping(value = "/operations",
method = RequestMethod.POST,
@ -53,7 +57,13 @@ public class OperationResource {
}
/**
* PUT /operations -> Updates an existing operation.
* PUT /operations : Updates an existing operation.
*
* @param operation the operation to update
* @return the ResponseEntity with status 200 (OK) and with body the updated operation,
* or with status 400 (Bad Request) if the operation is not valid,
* or with status 500 (Internal Server Error) if the operation couldnt be updated
* @throws URISyntaxException if the Location URI syntax is incorrect
*/
@RequestMapping(value = "/operations",
method = RequestMethod.PUT,
@ -71,7 +81,11 @@ public class OperationResource {
}
/**
* GET /operations -> get all the operations.
* GET /operations : get all the operations.
*
* @param pageable the pagination information
* @return the ResponseEntity with status 200 (OK) and the list of operations in body
* @throws URISyntaxException if there is an error to generate the pagination HTTP headers
*/
@RequestMapping(value = "/operations",
method = RequestMethod.GET,
@ -86,7 +100,10 @@ public class OperationResource {
}
/**
* GET /operations/:id -> get the "id" operation.
* GET /operations/:id : get the "id" operation.
*
* @param id the id of the operation to retrieve
* @return the ResponseEntity with status 200 (OK) and with body the operation, or with status 404 (Not Found)
*/
@RequestMapping(value = "/operations/{id}",
method = RequestMethod.GET,
@ -103,7 +120,10 @@ public class OperationResource {
}
/**
* DELETE /operations/:id -> delete the "id" operation.
* DELETE /operations/:id : delete the "id" operation.
*
* @param id the id of the operation to delete
* @return the ResponseEntity with status 200 (OK)
*/
@RequestMapping(value = "/operations/{id}",
method = RequestMethod.DELETE,
@ -114,4 +134,5 @@ public class OperationResource {
operationRepository.delete(id);
return ResponseEntity.ok().headers(HeaderUtil.createEntityDeletionAlert("operation", id.toString())).build();
}
}

View File

@ -52,7 +52,7 @@ import java.util.stream.Collectors;
* but then all authorities come from the cache, so in fact it's much better than doing an outer join
* (which will get lots of data from the database, for each HTTP call).</li>
* <li> As this manages users, for security reasons, we'd rather have a DTO layer.</li>
* </p>
* </ul>
* <p>Another option would be to have a specific JPA entity graph to handle this case.</p>
*/
@RestController
@ -75,12 +75,17 @@ public class UserResource {
private UserService userService;
/**
* POST /users -> Creates a new user.
* POST /users : Creates a new user.
* <p>
* Creates a new user if the login and email are not already used, and sends an
* mail with an activation link.
* The user needs to be activated on creation.
* </p>
*
* @param managedUserDTO the user to create
* @param request the HTTP request
* @return the ResponseEntity with status 201 (Created) and with body the new user, or with status 400 (Bad Request) if the login or email is already in use
* @throws URISyntaxException if the Location URI syntaxt is incorrect
*/
@RequestMapping(value = "/users",
method = RequestMethod.POST,
@ -91,11 +96,11 @@ public class UserResource {
log.debug("REST request to save User : {}", managedUserDTO);
if (userRepository.findOneByLogin(managedUserDTO.getLogin()).isPresent()) {
return ResponseEntity.badRequest()
.headers(HeaderUtil.createFailureAlert("user-management", "userexists", "Login already in use"))
.headers(HeaderUtil.createFailureAlert("userManagement", "userexists", "Login already in use"))
.body(null);
} else if (userRepository.findOneByEmail(managedUserDTO.getEmail()).isPresent()) {
return ResponseEntity.badRequest()
.headers(HeaderUtil.createFailureAlert("user-management", "emailexists", "Email already in use"))
.headers(HeaderUtil.createFailureAlert("userManagement", "emailexists", "Email already in use"))
.body(null);
} else {
User newUser = userService.createUser(managedUserDTO);
@ -107,13 +112,18 @@ public class UserResource {
request.getContextPath(); // "/myContextPath" or "" if deployed in root context
mailService.sendCreationEmail(newUser, baseUrl);
return ResponseEntity.created(new URI("/api/users/" + newUser.getLogin()))
.headers(HeaderUtil.createAlert( "user-management.created", newUser.getLogin()))
.headers(HeaderUtil.createAlert( "userManagement.created", newUser.getLogin()))
.body(newUser);
}
}
/**
* PUT /users -> Updates an existing User.
* PUT /users : Updates an existing User.
*
* @param managedUserDTO the user to update
* @return the ResponseEntity with status 200 (OK) and with body the updated user,
* or with status 400 (Bad Request) if the login or email is already in use,
* or with status 500 (Internal Server Error) if the user couldnt be updated
*/
@RequestMapping(value = "/users",
method = RequestMethod.PUT,
@ -121,15 +131,15 @@ public class UserResource {
@Timed
@Transactional
@Secured(AuthoritiesConstants.ADMIN)
public ResponseEntity<ManagedUserDTO> updateUser(@RequestBody ManagedUserDTO managedUserDTO) throws URISyntaxException {
public ResponseEntity<ManagedUserDTO> updateUser(@RequestBody ManagedUserDTO managedUserDTO) {
log.debug("REST request to update User : {}", managedUserDTO);
Optional<User> existingUser = userRepository.findOneByEmail(managedUserDTO.getEmail());
if (existingUser.isPresent() && (!existingUser.get().getId().equals(managedUserDTO.getId()))) {
return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert("user-management", "emailexists", "E-mail already in use")).body(null);
return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert("userManagement", "emailexists", "E-mail already in use")).body(null);
}
existingUser = userRepository.findOneByLogin(managedUserDTO.getLogin());
if (existingUser.isPresent() && (!existingUser.get().getId().equals(managedUserDTO.getId()))) {
return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert("user-management", "userexists", "Login already in use")).body(null);
return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert("userManagement", "userexists", "Login already in use")).body(null);
}
return userRepository
.findOneById(managedUserDTO.getId())
@ -146,7 +156,7 @@ public class UserResource {
authority -> authorities.add(authorityRepository.findOne(authority))
);
return ResponseEntity.ok()
.headers(HeaderUtil.createAlert("user-management.updated", managedUserDTO.getLogin()))
.headers(HeaderUtil.createAlert("userManagement.updated", managedUserDTO.getLogin()))
.body(new ManagedUserDTO(userRepository
.findOne(managedUserDTO.getId())));
})
@ -155,7 +165,11 @@ public class UserResource {
}
/**
* GET /users -> get all users.
* GET /users : get all users.
*
* @param pageable the pagination information
* @return the ResponseEntity with status 200 (OK) and with body all users
* @throws URISyntaxException if the pagination headers couldnt be generated
*/
@RequestMapping(value = "/users",
method = RequestMethod.GET,
@ -166,14 +180,17 @@ public class UserResource {
throws URISyntaxException {
Page<User> page = userRepository.findAll(pageable);
List<ManagedUserDTO> managedUserDTOs = page.getContent().stream()
.map(user -> new ManagedUserDTO(user))
.map(ManagedUserDTO::new)
.collect(Collectors.toList());
HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/users");
return new ResponseEntity<>(managedUserDTOs, headers, HttpStatus.OK);
}
/**
* GET /users/:login -> get the "login" user.
* GET /users/:login : get the "login" user.
*
* @param login the login of the user to find
* @return the ResponseEntity with status 200 (OK) and with body the "login" user, or with status 404 (Not Found)
*/
@RequestMapping(value = "/users/{login:[_'.@a-z0-9-]+}",
method = RequestMethod.GET,
@ -187,9 +204,12 @@ public class UserResource {
.orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND));
}
/**
* DELETE USER :login -> delete the "login" User.
* DELETE USER :login : delete the "login" User.
*
* @param login the login of the user to delete
* @return the ResponseEntity with status 200 (OK)
*/
@RequestMapping(value = "/users/{login}",
@RequestMapping(value = "/users/{login:[_'.@a-z0-9-]+}",
method = RequestMethod.DELETE,
produces = MediaType.APPLICATION_JSON_VALUE)
@Timed
@ -197,6 +217,6 @@ public class UserResource {
public ResponseEntity<Void> deleteUser(@PathVariable String login) {
log.debug("REST request to delete User: {}", login);
userService.deleteUserInformation(login);
return ResponseEntity.ok().headers(HeaderUtil.createAlert( "user-management.deleted", login)).build();
return ResponseEntity.ok().headers(HeaderUtil.createAlert( "userManagement.deleted", login)).build();
}
}

View File

@ -13,7 +13,7 @@ import java.util.stream.Collectors;
*/
public class UserDTO {
public static final int PASSWORD_MIN_LENGTH = 5;
public static final int PASSWORD_MIN_LENGTH = 4;
public static final int PASSWORD_MAX_LENGTH = 100;
@Pattern(regexp = "^[a-z0-9]*$")

View File

@ -6,6 +6,7 @@ public final class ErrorConstants {
public static final String ERR_ACCESS_DENIED = "error.accessDenied";
public static final String ERR_VALIDATION = "error.validation";
public static final String ERR_METHOD_NOT_SUPPORTED = "error.methodNotSupported";
public static final String ERR_INTERNAL_SERVER_ERROR = "error.internalServerError";
private ErrorConstants() {
}

View File

@ -16,16 +16,16 @@ public class ErrorDTO implements Serializable {
private List<FieldErrorDTO> fieldErrors;
ErrorDTO(String message) {
public ErrorDTO(String message) {
this(message, null);
}
ErrorDTO(String message, String description) {
public ErrorDTO(String message, String description) {
this.message = message;
this.description = description;
}
ErrorDTO(String message, String description, List<FieldErrorDTO> fieldErrors) {
public ErrorDTO(String message, String description, List<FieldErrorDTO> fieldErrors) {
this.message = message;
this.description = description;
this.fieldErrors = fieldErrors;

View File

@ -2,8 +2,11 @@ package com.mycompany.myapp.web.rest.errors;
import java.util.List;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.ResponseEntity.BodyBuilder;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
@ -64,4 +67,19 @@ public class ExceptionTranslator {
public ErrorDTO processMethodNotSupportedException(HttpRequestMethodNotSupportedException exception) {
return new ErrorDTO(ErrorConstants.ERR_METHOD_NOT_SUPPORTED, exception.getMessage());
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorDTO> processRuntimeException(Exception ex) throws Exception {
BodyBuilder builder;
ErrorDTO errorDTO;
ResponseStatus responseStatus = AnnotationUtils.findAnnotation(ex.getClass(), ResponseStatus.class);
if (responseStatus != null) {
builder = ResponseEntity.status(responseStatus.value());
errorDTO = new ErrorDTO("error." + responseStatus.value().value(), responseStatus.reason());
} else {
builder = ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR);
errorDTO = new ErrorDTO(ErrorConstants.ERR_INTERNAL_SERVER_ERROR, "Internal server error");
}
return builder.body(errorDTO);
}
}

View File

@ -12,7 +12,7 @@ public class FieldErrorDTO implements Serializable {
private final String message;
FieldErrorDTO(String dto, String field, String message) {
public FieldErrorDTO(String dto, String field, String message) {
this.objectName = dto;
this.field = field;
this.message = message;

View File

@ -0,0 +1,56 @@
package com.mycompany.myapp.web.rest.mapper;
import com.mycompany.myapp.domain.Authority;
import com.mycompany.myapp.domain.User;
import com.mycompany.myapp.web.rest.dto.UserDTO;
import org.mapstruct.*;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Mapper for the entity User and its DTO UserDTO.
*/
@Mapper(componentModel = "spring", uses = {})
public interface UserMapper {
UserDTO userToUserDTO(User user);
List<UserDTO> usersToUserDTOs(List<User> users);
@Mapping(target = "createdBy", ignore = true)
@Mapping(target = "createdDate", ignore = true)
@Mapping(target = "lastModifiedBy", ignore = true)
@Mapping(target = "lastModifiedDate", ignore = true)
@Mapping(target = "persistentTokens", ignore = true)
@Mapping(target = "id", ignore = true)
@Mapping(target = "activationKey", ignore = true)
@Mapping(target = "resetKey", ignore = true)
@Mapping(target = "resetDate", ignore = true)
User userDTOToUser(UserDTO userDTO);
List<User> userDTOsToUsers(List<UserDTO> userDTOs);
default User userFromId(Long id) {
if (id == null) {
return null;
}
User user = new User();
user.setId(id);
return user;
}
default Set<String> stringsFromAuthorities (Set < Authority > authorities) {
return authorities.stream().map(Authority::getName)
.collect(Collectors.toSet());
}
default Set<Authority> authoritiesFromStrings(Set<String> strings) {
return strings.stream().map(string -> {
Authority auth = new Authority();
auth.setName(string);
return auth;
}).collect(Collectors.toSet());
}
}

View File

@ -3,34 +3,34 @@ package com.mycompany.myapp.web.rest.util;
import org.springframework.http.HttpHeaders;
/**
* Utility class for http header creation.
* Utility class for HTTP headers creation.
*
*/
public class HeaderUtil {
public static HttpHeaders createAlert(String message, String param) {
HttpHeaders headers = new HttpHeaders();
headers.add("X-sampleapplicationApp-alert", message);
headers.add("X-sampleapplicationApp-params", param);
headers.add("X-sampleApplicationApp-alert", message);
headers.add("X-sampleApplicationApp-params", param);
return headers;
}
public static HttpHeaders createEntityCreationAlert(String entityName, String param) {
return createAlert("sampleapplicationApp." + entityName + ".created", param);
return createAlert("sampleApplicationApp." + entityName + ".created", param);
}
public static HttpHeaders createEntityUpdateAlert(String entityName, String param) {
return createAlert("sampleapplicationApp." + entityName + ".updated", param);
return createAlert("sampleApplicationApp." + entityName + ".updated", param);
}
public static HttpHeaders createEntityDeletionAlert(String entityName, String param) {
return createAlert("sampleapplicationApp." + entityName + ".deleted", param);
return createAlert("sampleApplicationApp." + entityName + ".deleted", param);
}
public static HttpHeaders createFailureAlert(String entityName, String errorKey, String defaultMessage) {
HttpHeaders headers = new HttpHeaders();
headers.add("X-sampleapplicationApp-error", "error." + errorKey);
headers.add("X-sampleapplicationApp-params", entityName);
headers.add("X-sampleApplicationApp-error", "error." + errorKey);
headers.add("X-sampleApplicationApp-params", entityName);
return headers;
}
}

View File

@ -10,9 +10,8 @@ import java.net.URISyntaxException;
* Utility class for handling pagination.
*
* <p>
* Pagination uses the same principles as the <a href="https://developer.github.com/v3/#pagination">Github API</api>,
* Pagination uses the same principles as the <a href="https://developer.github.com/v3/#pagination">Github API</a>,
* and follow <a href="http://tools.ietf.org/html/rfc5988">RFC 5988 (Link header)</a>.
* </p>
*/
public class PaginationUtil {
@ -23,19 +22,19 @@ public class PaginationUtil {
headers.add("X-Total-Count", "" + page.getTotalElements());
String link = "";
if ((page.getNumber() + 1) < page.getTotalPages()) {
link = "<" + (new URI(baseUrl +"?page=" + (page.getNumber() + 1) + "&size=" + page.getSize())).toString() + ">; rel=\"next\",";
link = "<" + (new URI(baseUrl + "?page=" + (page.getNumber() + 1) + "&size=" + page.getSize())).toString() + ">; rel=\"next\",";
}
// prev link
if ((page.getNumber()) > 0) {
link += "<" + (new URI(baseUrl +"?page=" + (page.getNumber() - 1) + "&size=" + page.getSize())).toString() + ">; rel=\"prev\",";
link += "<" + (new URI(baseUrl + "?page=" + (page.getNumber() - 1) + "&size=" + page.getSize())).toString() + ">; rel=\"prev\",";
}
// last and first link
int lastPage = 0;
if (page.getTotalPages() > 0) {
lastPage = page.getTotalPages() - 1;
}
link += "<" + (new URI(baseUrl +"?page=" + lastPage + "&size=" + page.getSize())).toString() + ">; rel=\"last\",";
link += "<" + (new URI(baseUrl +"?page=" + 0 + "&size=" + page.getSize())).toString() + ">; rel=\"first\"";
link += "<" + (new URI(baseUrl + "?page=" + lastPage + "&size=" + page.getSize())).toString() + ">; rel=\"last\",";
link += "<" + (new URI(baseUrl + "?page=" + 0 + "&size=" + page.getSize())).toString() + ">; rel=\"first\"";
headers.add(HttpHeaders.LINK, link);
return headers;
}

View File

@ -10,16 +10,14 @@
# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
# ===================================================================
spring:
profiles:
active: dev
devtools:
restart:
enabled: true
livereload:
enabled: false # we use Grunt + BrowserSync for livereload
enabled: false # we use gulp + BrowserSync for livereload
datasource:
driver-class-name: org.h2.jdbcx.JdbcDataSource
url: jdbc:h2:mem:sampleapplication;DB_CLOSE_DELAY=-1
name:
username: sampleApplication
@ -71,3 +69,12 @@ jhipster:
host: localhost
port: 2003
prefix: sampleApplication
logs: # report metrics in the logs
enabled: false
reportFrequency: 60 # in seconds
logging:
logstash: # Forward logs to logstash over a socket, used by LoggingConfiguration
enabled: false
host: localhost
port: 5000
queueSize: 512

View File

@ -10,20 +10,24 @@
# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
# ===================================================================
spring:
profiles:
active: prod
devtools:
restart:
enabled: false
livereload:
enabled: false
datasource:
driver-class-name: com.mysql.jdbc.jdbc2.optional.MysqlDataSource
url: jdbc:mysql://localhost:3306/sampleApplication?useUnicode=true&characterEncoding=utf8
url: jdbc:mysql://localhost:3306/sampleApplication?useUnicode=true&characterEncoding=utf8&useSSL=false
name:
username: root
password:
hikari:
data-source-properties:
cachePrepStmts: true
prepStmtCacheSize: 250
prepStmtCacheSqlLimit: 2048
useServerPrepStmts: true
jpa:
database-platform: org.hibernate.dialect.MySQLInnoDBDialect
database: MYSQL
@ -57,7 +61,7 @@ server:
jhipster:
http:
cache: # Used by the CachingHttpHeadersFilter
timeToLiveInDays: 31
timeToLiveInDays: 1461
cache: # Hibernate 2nd level cache, used by CacheConfiguration
timeToLiveSeconds: 3600
ehcache:
@ -75,3 +79,12 @@ jhipster:
host: localhost
port: 2003
prefix: sampleApplication
logs: # report metrics in the logs
enabled: false
reportFrequency: 60 # in seconds
logging:
logstash: # Forward logs to logstash over a socket, used by LoggingConfiguration
enabled: false
host: localhost
port: 5000
queueSize: 512

View File

@ -11,14 +11,17 @@
# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
# ===================================================================
spring:
application:
name: sampleApplication
jpa:
open-in-view: false
hibernate:
ddl-auto: none
naming-strategy: org.springframework.boot.orm.jpa.hibernate.SpringNamingStrategy
messages:
basename: classpath:/i18n/messages
basename: i18n/messages
mvc:
favicon:
enabled: false
@ -48,7 +51,7 @@ jhipster:
mail:
from: sampleApplication@localhost
security:
rememberme:
rememberMe:
# security key (this key should be unique for your application, and kept secret)
key: 5c37379956bd1242f5636c8cb322c2966ad81277
swagger:
@ -56,6 +59,8 @@ jhipster:
description: sampleApplication API documentation
version: 0.0.1
termsOfServiceUrl:
contact:
contactName:
contactUrl:
contactEmail:
license:
licenseUrl:

View File

@ -19,6 +19,7 @@
The initial schema has the '00000000000001' id, so that it is over-written if we re-generate it.
-->
<changeSet id="00000000000001" author="jhipster">
<createTable tableName="jhi_user">
<column name="id" type="bigint" autoIncrement="${autoIncrement}">
<constraints primaryKey="true" nullable="false"/>
@ -30,7 +31,7 @@
<column name="first_name" type="varchar(50)"/>
<column name="last_name" type="varchar(50)"/>
<column name="email" type="varchar(100)">
<constraints unique="true"/>
<constraints unique="true" nullable="false"/>
</column>
<column name="activated" type="boolean" valueBoolean="false">
<constraints nullable="false" />
@ -110,7 +111,6 @@
constraintName="fk_user_persistent_token"
referencedColumnNames="id"
referencedTableName="jhi_user"/>
<loadData encoding="UTF-8"
file="config/liquibase/users.csv"
separator=";"
@ -129,7 +129,7 @@
file="config/liquibase/users_authorities.csv"
separator=";"
tableName="jhi_user_authority"/>
<createTable tableName="jhi_persistent_audit_event">
<column name="event_id" type="bigint" autoIncrement="${autoIncrement}">
<constraints primaryKey="true" nullable="false"/>

View File

@ -25,7 +25,9 @@
<constraints nullable="false" />
</column>
<column name="description" type="varchar(255)"/>
<column name="description" type="varchar(255)">
<constraints nullable="true" />
</column>
<column name="amount" type="decimal(10,2)">
<constraints nullable="false" />
@ -35,14 +37,12 @@
<!-- jhipster-needle-liquibase-add-column - Jhipster will add columns here, do not remove-->
</createTable>
<dropDefaultValue tableName="operation" columnName="date" columnDataType="datetime"/>
<addForeignKeyConstraint baseColumnNames="bank_account_id"
baseTableName="operation"
constraintName="fk_operation_bankaccount_id"
referencedColumnNames="id"
referencedTableName="bank_account"/>
<createTable tableName="operation_label">
<column name="labels_id" type="bigint">
<constraints nullable="false"/>

View File

@ -0,0 +1,22 @@
# Error page
error.title=Your request cannot be processed
error.subtitle=Sorry, an error has occurred.
error.status=Status:
error.message=Message:
# Activation e-mail
email.activation.title=sampleApplication account activation
email.activation.greeting=Dear {0}
email.activation.text1=Your sampleApplication account has been created, please click on the URL below to activate it:
email.activation.text2=Regards,
email.signature=sampleApplication Team.
# Creation email
email.creation.text1=Your sampleApplication account has been created, please click on the URL below to access it:
# Reset e-mail
email.reset.title=sampleApplication password reset
email.reset.greeting=Dear {0}
email.reset.text1=For your sampleApplication account a password reset was requested, please click on the URL below to reset it:
email.reset.text2=Regards,

View File

@ -11,6 +11,9 @@ email.activation.text1=Your sampleApplication account has been created, please c
email.activation.text2=Regards,
email.signature=sampleApplication Team.
# Creation email
email.creation.text1=Your sampleApplication account has been created, please click on the URL below to access it:
# Reset e-mail
email.reset.title=sampleApplication password reset
email.reset.greeting=Dear {0}

View File

@ -1,19 +0,0 @@
# Error page
error.title=Votre demande ne peut être traitée
error.subtitle=Désolé, une erreur s'est produite.
error.status=Status:
error.message=Message:
# Activation e-mail
email.activation.title=Activation de votre compte sampleApplication
email.activation.greeting=Cher {0}
email.activation.text1=Votre compte sampleApplication a été créé, pour l'activer merci de cliquer sur le lien ci-dessous:
email.activation.text2=Cordialement,
email.signature=sampleApplication.
# Reset e-mail
email.reset.title=sampleApplication Réinitialisation de mot de passe
email.reset.greeting=Cher {0}
email.reset.text1=Un nouveau mot de passe pour votre compte sampleApplication a été demandé, veuillez cliquer sur le lien ci-dessous pour le réinitialiser:
email.reset.text2=Cordialement,

View File

@ -30,9 +30,8 @@
<logger name="ch.qos.logback" level="WARN"/>
<logger name="com.codahale.metrics" level="WARN"/>
<logger name="com.ryantenney" level="WARN"/>
<logger name="com.sun.xml.internal.bind" level="WARN"/>
<logger name="com.sun" level="WARN"/>
<logger name="com.zaxxer" level="WARN"/>
<logger name="io.undertow" level="WARN"/>
<logger name="net.sf.ehcache" level="WARN"/>
<logger name="org.apache" level="WARN"/>
<logger name="org.apache.catalina.startup.DigesterFactory" level="OFF"/>

View File

@ -8,8 +8,8 @@
<p th:text="#{email.activation.greeting(${user.login})}">
Dear
</p>
<p th:text="#{email.activation.text1}">
Your JHipster account has been created, please click on the URL below to activate it:
<p th:text="#{email.creation.text1}">
Your JHipster account has been created, please click on the URL below to access it:
</p>
<p>
<a th:href="@{|${baseUrl}/#/reset/finish?key=${user.resetKey}|}"

View File

@ -1,543 +0,0 @@
# Apache Configuration File
# (!) Using `.htaccess` files slows down Apache, therefore, if you have access
# to the main server config file (usually called `httpd.conf`), you should add
# this logic there: http://httpd.apache.org/docs/current/howto/htaccess.html.
# ##############################################################################
# # CROSS-ORIGIN RESOURCE SHARING (CORS) #
# ##############################################################################
# ------------------------------------------------------------------------------
# | Cross-domain AJAX requests |
# ------------------------------------------------------------------------------
# Enable cross-origin AJAX requests.
# http://code.google.com/p/html5security/wiki/CrossOriginRequestSecurity
# http://enable-cors.org/
# <IfModule mod_headers.c>
# Header set Access-Control-Allow-Origin "*"
# </IfModule>
# ------------------------------------------------------------------------------
# | CORS-enabled images |
# ------------------------------------------------------------------------------
# Send the CORS header for images when browsers request it.
# https://developer.mozilla.org/en/CORS_Enabled_Image
# http://blog.chromium.org/2011/07/using-cross-domain-images-in-webgl-and.html
# http://hacks.mozilla.org/2011/11/using-cors-to-load-webgl-textures-from-cross-domain-images/
<IfModule mod_setenvif.c>
<IfModule mod_headers.c>
<FilesMatch "\.(gif|ico|jpe?g|png|svg|svgz|webp)$">
SetEnvIf Origin ":" IS_CORS
Header set Access-Control-Allow-Origin "*" env=IS_CORS
</FilesMatch>
</IfModule>
</IfModule>
# ------------------------------------------------------------------------------
# | Web fonts access |
# ------------------------------------------------------------------------------
# Allow access from all domains for web fonts
<IfModule mod_headers.c>
<FilesMatch "\.(eot|font.css|otf|ttc|ttf|woff)$">
Header set Access-Control-Allow-Origin "*"
</FilesMatch>
</IfModule>
# ##############################################################################
# # ERRORS #
# ##############################################################################
# ------------------------------------------------------------------------------
# | 404 error prevention for non-existing redirected folders |
# ------------------------------------------------------------------------------
# Prevent Apache from returning a 404 error for a rewrite if a directory
# with the same name does not exist.
# http://httpd.apache.org/docs/current/content-negotiation.html#multiviews
# http://www.webmasterworld.com/apache/3808792.htm
Options -MultiViews
# ------------------------------------------------------------------------------
# | Custom error messages / pages |
# ------------------------------------------------------------------------------
# You can customize what Apache returns to the client in case of an error (see
# http://httpd.apache.org/docs/current/mod/core.html#errordocument), e.g.:
ErrorDocument 404 /404.html
# ##############################################################################
# # INTERNET EXPLORER #
# ##############################################################################
# ------------------------------------------------------------------------------
# | Better website experience |
# ------------------------------------------------------------------------------
# Force IE to render pages in the highest available mode in the various
# cases when it may not: http://hsivonen.iki.fi/doctype/ie-mode.pdf.
<IfModule mod_headers.c>
Header set X-UA-Compatible "IE=edge"
# `mod_headers` can't match based on the content-type, however, we only
# want to send this header for HTML pages and not for the other resources
<FilesMatch "\.(appcache|crx|css|eot|gif|htc|ico|jpe?g|js|m4a|m4v|manifest|mp4|oex|oga|ogg|ogv|otf|pdf|png|safariextz|svg|svgz|ttf|vcf|webapp|webm|webp|woff|xml|xpi)$">
Header unset X-UA-Compatible
</FilesMatch>
</IfModule>
# ------------------------------------------------------------------------------
# | Cookie setting from iframes |
# ------------------------------------------------------------------------------
# Allow cookies to be set from iframes in IE.
# <IfModule mod_headers.c>
# Header set P3P "policyref=\"/w3c/p3p.xml\", CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\""
# </IfModule>
# ------------------------------------------------------------------------------
# | Screen flicker |
# ------------------------------------------------------------------------------
# Stop screen flicker in IE on CSS rollovers (this only works in
# combination with the `ExpiresByType` directives for images from below).
# BrowserMatch "MSIE" brokenvary=1
# BrowserMatch "Mozilla/4.[0-9]{2}" brokenvary=1
# BrowserMatch "Opera" !brokenvary
# SetEnvIf brokenvary 1 force-no-vary
# ##############################################################################
# # MIME TYPES AND ENCODING #
# ##############################################################################
# ------------------------------------------------------------------------------
# | Proper MIME types for all files |
# ------------------------------------------------------------------------------
<IfModule mod_mime.c>
# Audio
AddType audio/mp4 m4a f4a f4b
AddType audio/ogg oga ogg
# JavaScript
# Normalize to standard type (it's sniffed in IE anyways):
# http://tools.ietf.org/html/rfc4329#section-7.2
AddType application/javascript js jsonp
AddType application/json json
# Video
AddType video/mp4 mp4 m4v f4v f4p
AddType video/ogg ogv
AddType video/webm webm
AddType video/x-flv flv
# Web fonts
AddType application/font-woff woff
AddType application/vnd.ms-fontobject eot
# Browsers usually ignore the font MIME types and sniff the content,
# however, Chrome shows a warning if other MIME types are used for the
# following fonts.
AddType application/x-font-ttf ttc ttf
AddType font/opentype otf
# Make SVGZ fonts work on iPad:
# https://twitter.com/FontSquirrel/status/14855840545
AddType image/svg+xml svg svgz
AddEncoding gzip svgz
# Other
AddType application/octet-stream safariextz
AddType application/x-chrome-extension crx
AddType application/x-opera-extension oex
AddType application/x-shockwave-flash swf
AddType application/x-web-app-manifest+json webapp
AddType application/x-xpinstall xpi
AddType application/xml atom rdf rss xml
AddType image/webp webp
AddType image/x-icon ico
AddType text/cache-manifest appcache manifest
AddType text/vtt vtt
AddType text/x-component htc
AddType text/x-vcard vcf
</IfModule>
# ------------------------------------------------------------------------------
# | UTF-8 encoding |
# ------------------------------------------------------------------------------
# Use UTF-8 encoding for anything served as `text/html` or `text/plain`.
AddDefaultCharset utf-8
# Force UTF-8 for certain file formats.
<IfModule mod_mime.c>
AddCharset utf-8 .atom .css .js .json .rss .vtt .webapp .xml
</IfModule>
# ##############################################################################
# # URL REWRITES #
# ##############################################################################
# ------------------------------------------------------------------------------
# | Rewrite engine |
# ------------------------------------------------------------------------------
# Turning on the rewrite engine and enabling the `FollowSymLinks` option is
# necessary for the following directives to work.
# If your web host doesn't allow the `FollowSymlinks` option, you may need to
# comment it out and use `Options +SymLinksIfOwnerMatch` but, be aware of the
# performance impact: http://httpd.apache.org/docs/current/misc/perf-tuning.html#symlinks
# Also, some cloud hosting services require `RewriteBase` to be set:
# http://www.rackspace.com/knowledge_center/frequently-asked-question/why-is-mod-rewrite-not-working-on-my-site
<IfModule mod_rewrite.c>
Options +FollowSymlinks
# Options +SymLinksIfOwnerMatch
RewriteEngine On
# RewriteBase /
</IfModule>
# ------------------------------------------------------------------------------
# | Suppressing / Forcing the "www." at the beginning of URLs |
# ------------------------------------------------------------------------------
# The same content should never be available under two different URLs especially
# not with and without "www." at the beginning. This can cause SEO problems
# (duplicate content), therefore, you should choose one of the alternatives and
# redirect the other one.
# By default option 1 (no "www.") is activated:
# http://no-www.org/faq.php?q=class_b
# If you'd prefer to use option 2, just comment out all the lines from option 1
# and uncomment the ones from option 2.
# IMPORTANT: NEVER USE BOTH RULES AT THE SAME TIME!
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Option 1: rewrite www.example.com → example.com
<IfModule mod_rewrite.c>
RewriteCond %{HTTPS} !=on
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L]
</IfModule>
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Option 2: rewrite example.com → www.example.com
# Be aware that the following might not be a good idea if you use "real"
# subdomains for certain parts of your website.
# <IfModule mod_rewrite.c>
# RewriteCond %{HTTPS} !=on
# RewriteCond %{HTTP_HOST} !^www\..+$ [NC]
# RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
# </IfModule>
# ##############################################################################
# # SECURITY #
# ##############################################################################
# ------------------------------------------------------------------------------
# | Content Security Policy (CSP) |
# ------------------------------------------------------------------------------
# You can mitigate the risk of cross-site scripting and other content-injection
# attacks by setting a Content Security Policy which whitelists trusted sources
# of content for your site.
# The example header below allows ONLY scripts that are loaded from the current
# site's origin (no inline scripts, no CDN, etc). This almost certainly won't
# work as-is for your site!
# To get all the details you'll need to craft a reasonable policy for your site,
# read: http://html5rocks.com/en/tutorials/security/content-security-policy (or
# see the specification: http://w3.org/TR/CSP).
# <IfModule mod_headers.c>
# Header set Content-Security-Policy "script-src 'self'; object-src 'self'"
# <FilesMatch "\.(appcache|crx|css|eot|gif|htc|ico|jpe?g|js|m4a|m4v|manifest|mp4|oex|oga|ogg|ogv|otf|pdf|png|safariextz|svg|svgz|ttf|vcf|webapp|webm|webp|woff|xml|xpi)$">
# Header unset Content-Security-Policy
# </FilesMatch>
# </IfModule>
# ------------------------------------------------------------------------------
# | File access |
# ------------------------------------------------------------------------------
# Block access to directories without a default document.
# Usually you should leave this uncommented because you shouldn't allow anyone
# to surf through every directory on your server (which may includes rather
# private places like the CMS's directories).
<IfModule mod_autoindex.c>
Options -Indexes
</IfModule>
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Block access to hidden files and directories.
# This includes directories used by version control systems such as Git and SVN.
<IfModule mod_rewrite.c>
RewriteCond %{SCRIPT_FILENAME} -d [OR]
RewriteCond %{SCRIPT_FILENAME} -f
RewriteRule "(^|/)\." - [F]
</IfModule>
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Block access to backup and source files.
# These files may be left by some text editors and can pose a great security
# danger when anyone has access to them.
<FilesMatch "(^#.*#|\.(bak|config|dist|fla|inc|ini|log|psd|sh|sql|sw[op])|~)$">
Order allow,deny
Deny from all
Satisfy All
</FilesMatch>
# ------------------------------------------------------------------------------
# | Secure Sockets Layer (SSL) |
# ------------------------------------------------------------------------------
# Rewrite secure requests properly to prevent SSL certificate warnings, e.g.:
# prevent `https://www.example.com` when your certificate only allows
# `https://secure.example.com`.
# <IfModule mod_rewrite.c>
# RewriteCond %{SERVER_PORT} !^443
# RewriteRule ^ https://example-domain-please-change-me.com%{REQUEST_URI} [R=301,L]
# </IfModule>
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Force client-side SSL redirection.
# If a user types "example.com" in his browser, the above rule will redirect him
# to the secure version of the site. That still leaves a window of opportunity
# (the initial HTTP connection) for an attacker to downgrade or redirect the
# request. The following header ensures that browser will ONLY connect to your
# server via HTTPS, regardless of what the users type in the address bar.
# http://www.html5rocks.com/en/tutorials/security/transport-layer-security/
# <IfModule mod_headers.c>
# Header set Strict-Transport-Security max-age=16070400;
# </IfModule>
# ------------------------------------------------------------------------------
# | Server software information |
# ------------------------------------------------------------------------------
# Avoid displaying the exact Apache version number, the description of the
# generic OS-type and the information about Apache's compiled-in modules.
# ADD THIS DIRECTIVE IN THE `httpd.conf` AS IT WILL NOT WORK IN THE `.htaccess`!
# ServerTokens Prod
# ##############################################################################
# # WEB PERFORMANCE #
# ##############################################################################
# ------------------------------------------------------------------------------
# | Compression |
# ------------------------------------------------------------------------------
<IfModule mod_deflate.c>
# Force compression for mangled headers.
# http://developer.yahoo.com/blogs/ydn/posts/2010/12/pushing-beyond-gzipping
<IfModule mod_setenvif.c>
<IfModule mod_headers.c>
SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding
RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding
</IfModule>
</IfModule>
# Compress all output labeled with one of the following MIME-types
# (for Apache versions below 2.3.7, you don't need to enable `mod_filter`
# and can remove the `<IfModule mod_filter.c>` and `</IfModule>` lines
# as `AddOutputFilterByType` is still in the core directives).
<IfModule mod_filter.c>
AddOutputFilterByType DEFLATE application/atom+xml \
application/javascript \
application/json \
application/rss+xml \
application/vnd.ms-fontobject \
application/x-font-ttf \
application/x-web-app-manifest+json \
application/xhtml+xml \
application/xml \
font/opentype \
image/svg+xml \
image/x-icon \
text/css \
text/html \
text/plain \
text/x-component \
text/xml
</IfModule>
</IfModule>
# ------------------------------------------------------------------------------
# | Content transformations |
# ------------------------------------------------------------------------------
# Prevent some of the mobile network providers from modifying the content of
# your site: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.5.
# <IfModule mod_headers.c>
# Header set Cache-Control "no-transform"
# </IfModule>
# ------------------------------------------------------------------------------
# | ETag removal |
# ------------------------------------------------------------------------------
# Since we're sending far-future expires headers (see below), ETags can
# be removed: http://developer.yahoo.com/performance/rules.html#etags.
# `FileETag None` is not enough for every server.
<IfModule mod_headers.c>
Header unset ETag
</IfModule>
FileETag None
# ------------------------------------------------------------------------------
# | Expires headers (for better cache control) |
# ------------------------------------------------------------------------------
# The following expires headers are set pretty far in the future. If you don't
# control versioning with filename-based cache busting, consider lowering the
# cache time for resources like CSS and JS to something like 1 week.
<IfModule mod_expires.c>
ExpiresActive on
ExpiresDefault "access plus 1 month"
# CSS
ExpiresByType text/css "access plus 1 year"
# Data interchange
ExpiresByType application/json "access plus 0 seconds"
ExpiresByType application/xml "access plus 0 seconds"
ExpiresByType text/xml "access plus 0 seconds"
# Favicon (cannot be renamed!)
ExpiresByType image/x-icon "access plus 1 week"
# HTML components (HTCs)
ExpiresByType text/x-component "access plus 1 month"
# HTML
ExpiresByType text/html "access plus 0 seconds"
# JavaScript
ExpiresByType application/javascript "access plus 1 year"
# Manifest files
ExpiresByType application/x-web-app-manifest+json "access plus 0 seconds"
ExpiresByType text/cache-manifest "access plus 0 seconds"
# Media
ExpiresByType audio/ogg "access plus 1 month"
ExpiresByType image/gif "access plus 1 month"
ExpiresByType image/jpeg "access plus 1 month"
ExpiresByType image/png "access plus 1 month"
ExpiresByType video/mp4 "access plus 1 month"
ExpiresByType video/ogg "access plus 1 month"
ExpiresByType video/webm "access plus 1 month"
# Web feeds
ExpiresByType application/atom+xml "access plus 1 hour"
ExpiresByType application/rss+xml "access plus 1 hour"
# Web fonts
ExpiresByType application/font-woff "access plus 1 month"
ExpiresByType application/vnd.ms-fontobject "access plus 1 month"
ExpiresByType application/x-font-ttf "access plus 1 month"
ExpiresByType font/opentype "access plus 1 month"
ExpiresByType image/svg+xml "access plus 1 month"
</IfModule>
# ------------------------------------------------------------------------------
# | Filename-based cache busting |
# ------------------------------------------------------------------------------
# If you're not using a build process to manage your filename version revving,
# you might want to consider enabling the following directives to route all
# requests such as `/css/style.12345.css` to `/css/style.css`.
# To understand why this is important and a better idea than `*.css?v231`, read:
# http://stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring
# <IfModule mod_rewrite.c>
# RewriteCond %{REQUEST_FILENAME} !-f
# RewriteCond %{REQUEST_FILENAME} !-d
# RewriteRule ^(.+)\.(\d+)\.(js|css|png|jpg|gif)$ $1.$3 [L]
# </IfModule>
# ------------------------------------------------------------------------------
# | File concatenation |
# ------------------------------------------------------------------------------
# Allow concatenation from within specific CSS and JS files, e.g.:
# Inside of `script.combined.js` you could have
# <!--#include file="libs/jquery.js" -->
# <!--#include file="plugins/jquery.idletimer.js" -->
# and they would be included into this single file.
# <IfModule mod_include.c>
# <FilesMatch "\.combined\.js$">
# Options +Includes
# AddOutputFilterByType INCLUDES application/javascript application/json
# SetOutputFilter INCLUDES
# </FilesMatch>
# <FilesMatch "\.combined\.css$">
# Options +Includes
# AddOutputFilterByType INCLUDES text/css
# SetOutputFilter INCLUDES
# </FilesMatch>
# </IfModule>
# ------------------------------------------------------------------------------
# | Persistent connections |
# ------------------------------------------------------------------------------
# Allow multiple requests to be sent over the same TCP connection:
# http://httpd.apache.org/docs/current/en/mod/core.html#keepalive.
# Enable if you serve a lot of static content but, be aware of the
# possible disadvantages!
# <IfModule mod_headers.c>
# Header set Connection Keep-Alive
# </IfModule>

View File

@ -0,0 +1,16 @@
(function() {
'use strict';
angular
.module('sampleApplicationApp')
.config(stateConfig);
stateConfig.$inject = ['$stateProvider'];
function stateConfig($stateProvider) {
$stateProvider.state('account', {
abstract: true,
parent: 'app'
});
}
})();

View File

@ -0,0 +1,23 @@
(function() {
'use strict';
angular
.module('sampleApplicationApp')
.controller('ActivationController', ActivationController);
ActivationController.$inject = ['$stateParams', 'Auth', 'LoginService'];
function ActivationController ($stateParams, Auth, LoginService) {
var vm = this;
Auth.activateAccount({key: $stateParams.key}).then(function () {
vm.error = null;
vm.success = 'OK';
}).catch(function () {
vm.success = null;
vm.error = 'ERROR';
});
vm.login = LoginService.open;
}
})();

View File

@ -3,11 +3,11 @@
<div class="col-md-8 col-md-offset-2">
<h1 translate="activate.title">Activation</h1>
<div class="alert alert-success" ng-show="success" translate="activate.messages.success">
<strong>Your user has been activated.</strong> Please <a class="alert-link" href="#/login">sign in</a>.
<div class="alert alert-success" ng-show="vm.success" translate="activate.messages.success" translate-compile>
<strong>Your user has been activated.</strong> Please <a class="alert-link" href="" ng-click="vm.login">sign in</a>.
</div>
<div class="alert alert-danger" ng-show="error" translate="activate.messages.error">
<div class="alert alert-danger" ng-show="vm.error" translate="activate.messages.error">
<strong>Your user could not be activated.</strong> Please use the registration form to sign up.
</div>

View File

@ -0,0 +1,33 @@
(function() {
'use strict';
angular
.module('sampleApplicationApp')
.config(stateConfig);
stateConfig.$inject = ['$stateProvider'];
function stateConfig($stateProvider) {
$stateProvider.state('activate', {
parent: 'account',
url: '/activate?key',
data: {
authorities: [],
pageTitle: 'activate.title'
},
views: {
'content@': {
templateUrl: 'app/account/activate/activate.html',
controller: 'ActivationController',
controllerAs: 'vm'
}
},
resolve: {
translatePartialLoader: ['$translate', '$translatePartialLoader', function ($translate, $translatePartialLoader) {
$translatePartialLoader.addPart('activate');
return $translate.refresh();
}]
}
});
}
})();

View File

@ -0,0 +1,95 @@
/* globals $ */
(function() {
'use strict';
angular
.module('sampleApplicationApp')
.directive('passwordStrengthBar', passwordStrengthBar);
function passwordStrengthBar () {
var directive = {
replace: true,
restrict: 'E',
template: '<div id="strength">' +
'<small translate="global.messages.validate.newpassword.strength">Password strength:</small>' +
'<ul id="strengthBar">' +
'<li class="point"></li><li class="point"></li><li class="point"></li><li class="point"></li><li class="point"></li>' +
'</ul>' +
'</div>',
scope: {
passwordToCheck: '='
},
link: linkFunc
};
return directive;
/* private helper methods*/
function linkFunc(scope, iElement) {
var strength = {
colors: ['#F00', '#F90', '#FF0', '#9F0', '#0F0'],
mesureStrength: function (p) {
var _force = 0;
var _regex = /[$-/:-?{-~!"^_`\[\]]/g; // "
var _lowerLetters = /[a-z]+/.test(p);
var _upperLetters = /[A-Z]+/.test(p);
var _numbers = /[0-9]+/.test(p);
var _symbols = _regex.test(p);
var _flags = [_lowerLetters, _upperLetters, _numbers, _symbols];
var _passedMatches = $.grep(_flags, function (el) {
return el === true;
}).length;
_force += 2 * p.length + ((p.length >= 10) ? 1 : 0);
_force += _passedMatches * 10;
// penality (short password)
_force = (p.length <= 6) ? Math.min(_force, 10) : _force;
// penality (poor variety of characters)
_force = (_passedMatches === 1) ? Math.min(_force, 10) : _force;
_force = (_passedMatches === 2) ? Math.min(_force, 20) : _force;
_force = (_passedMatches === 3) ? Math.min(_force, 40) : _force;
return _force;
},
getColor: function (s) {
var idx = 0;
if (s <= 10) {
idx = 0;
}
else if (s <= 20) {
idx = 1;
}
else if (s <= 30) {
idx = 2;
}
else if (s <= 40) {
idx = 3;
}
else {
idx = 4;
}
return { idx: idx + 1, col: this.colors[idx] };
}
};
scope.$watch('passwordToCheck', function (password) {
if (password) {
var c = strength.getColor(strength.mesureStrength(password));
iElement.removeClass('ng-hide');
iElement.find('ul').children('li')
.css({ 'background-color': '#DDD' })
.slice(0, c.idx)
.css({ 'background-color': c.col });
}
});
}
}
})();

View File

@ -0,0 +1,39 @@
(function() {
'use strict';
angular
.module('sampleApplicationApp')
.controller('PasswordController', PasswordController);
PasswordController.$inject = ['Auth', 'Principal'];
function PasswordController (Auth, Principal) {
var vm = this;
vm.changePassword = changePassword;
vm.doNotMatch = null;
vm.error = null;
vm.success = null;
Principal.identity().then(function(account) {
vm.account = account;
});
function changePassword () {
if (vm.password !== vm.confirmPassword) {
vm.error = null;
vm.success = null;
vm.doNotMatch = 'ERROR';
} else {
vm.doNotMatch = null;
Auth.changePassword(vm.password).then(function () {
vm.error = null;
vm.success = 'OK';
}).catch(function () {
vm.success = null;
vm.error = 'ERROR';
});
}
}
}
})();

View File

@ -1,25 +1,25 @@
<div>
<div class="row">
<div class="col-md-8 col-md-offset-2">
<h2 translate="password.title" translate-values="{username: '{{account.login}}'}">Password for [<b>{{account.login}}</b>]</h2>
<h2 translate="password.title" translate-values="{username: '{{vm.account.login}}'}">Password for [<b>{{vm.account.login}}</b>]</h2>
<div class="alert alert-success" ng-show="success" translate="password.messages.success">
<div class="alert alert-success" ng-show="vm.success" translate="password.messages.success">
<strong>Password changed!</strong>
</div>
<div class="alert alert-danger" ng-show="error" translate="password.messages.error">
<div class="alert alert-danger" ng-show="vm.error" translate="password.messages.error">
<strong>An error has occurred!</strong> The password could not be changed.
</div>
<div class="alert alert-danger" ng-show="doNotMatch" translate="global.messages.error.dontmatch">
<div class="alert alert-danger" ng-show="vm.doNotMatch" translate="global.messages.error.dontmatch">
The password and its confirmation do not match!
</div>
<form name="form" role="form" novalidate ng-submit="changePassword()" show-validation>
<form name="form" role="form" novalidate ng-submit="vm.changePassword()" show-validation>
<div class="form-group">
<label class="control-label" for="password" translate="global.form.newpassword">New password</label>
<input type="password" class="form-control" id="password" name="password" placeholder="{{'global.form.newpassword.placeholder' | translate}}"
ng-model="password" ng-minlength=5 ng-maxlength=50 required>
ng-model="vm.password" ng-minlength=5 ng-maxlength=50 required>
<div ng-show="form.password.$dirty && form.password.$invalid">
<p class="help-block"
ng-show="form.password.$error.required" translate="global.messages.validate.newpassword.required">
@ -34,12 +34,12 @@
Your password cannot be longer than 50 characters.
</p>
</div>
<password-strength-bar password-to-check="password"></password-strength-bar>
<password-strength-bar password-to-check="vm.password"></password-strength-bar>
</div>
<div class="form-group">
<label class="control-label" for="confirmPassword" translate="global.form.confirmpassword">New password confirmation</label>
<input type="password" class="form-control" id="confirmPassword" name="confirmPassword" placeholder="{{'global.form.confirmpassword.placeholder' | translate}}"
ng-model="confirmPassword" ng-minlength=5 ng-maxlength=50 required>
ng-model="vm.confirmPassword" ng-minlength=5 ng-maxlength=50 required>
<div ng-show="form.confirmPassword.$dirty && form.confirmPassword.$invalid">
<p class="help-block"
ng-show="form.confirmPassword.$error.required" translate="global.messages.validate.confirmpassword.required">

View File

@ -0,0 +1,33 @@
(function() {
'use strict';
angular
.module('sampleApplicationApp')
.config(stateConfig);
stateConfig.$inject = ['$stateProvider'];
function stateConfig($stateProvider) {
$stateProvider.state('password', {
parent: 'account',
url: '/password',
data: {
authorities: ['ROLE_USER'],
pageTitle: 'global.menu.account.password'
},
views: {
'content@': {
templateUrl: 'app/account/password/password.html',
controller: 'PasswordController',
controllerAs: 'vm'
}
},
resolve: {
translatePartialLoader: ['$translate', '$translatePartialLoader', function ($translate, $translatePartialLoader) {
$translatePartialLoader.addPart('password');
return $translate.refresh();
}]
}
});
}
})();

View File

@ -0,0 +1,49 @@
(function() {
'use strict';
angular
.module('sampleApplicationApp')
.controller('RegisterController', RegisterController);
RegisterController.$inject = ['$translate', '$timeout', 'Auth', 'LoginService'];
function RegisterController ($translate, $timeout, Auth, LoginService) {
var vm = this;
vm.doNotMatch = null;
vm.error = null;
vm.errorUserExists = null;
vm.login = LoginService.open;
vm.register = register;
vm.registerAccount = {};
vm.success = null;
$timeout(function (){angular.element('[ng-model="vm.registerAccount.login"]').focus();});
function register () {
if (vm.registerAccount.password !== vm.confirmPassword) {
vm.doNotMatch = 'ERROR';
} else {
vm.registerAccount.langKey = $translate.use();
vm.doNotMatch = null;
vm.error = null;
vm.errorUserExists = null;
vm.errorEmailExists = null;
Auth.createAccount(vm.registerAccount).then(function () {
vm.success = 'OK';
}).catch(function (response) {
vm.success = null;
if (response.status === 400 && response.data === 'login already in use') {
vm.errorUserExists = 'ERROR';
} else if (response.status === 400 && response.data === 'e-mail address already in use') {
vm.errorEmailExists = 'ERROR';
} else {
vm.error = 'ERROR';
}
});
}
}
}
})();

View File

@ -3,33 +3,33 @@
<div class="col-md-8 col-md-offset-2">
<h1 translate="register.title">Registration</h1>
<div class="alert alert-success" ng-show="success" translate="register.messages.success">
<div class="alert alert-success" ng-show="vm.success" translate="register.messages.success">
<strong>Registration saved!</strong> Please check your email for confirmation.
</div>
<div class="alert alert-danger" ng-show="error" translate="register.messages.error.fail">
<div class="alert alert-danger" ng-show="vm.error" translate="register.messages.error.fail">
<strong>Registration failed!</strong> Please try again later.
</div>
<div class="alert alert-danger" ng-show="errorUserExists" translate="register.messages.error.userexists">
<div class="alert alert-danger" ng-show="vm.errorUserExists" translate="register.messages.error.userexists">
<strong>Login name already registered!</strong> Please choose another one.
</div>
<div class="alert alert-danger" ng-show="errorEmailExists" translate="register.messages.error.emailexists">
<div class="alert alert-danger" ng-show="vm.errorEmailExists" translate="register.messages.error.emailexists">
<strong>E-mail is already in use!</strong> Please choose another one.
</div>
<div class="alert alert-danger" ng-show="doNotMatch" translate="global.messages.error.dontmatch">
<div class="alert alert-danger" ng-show="vm.doNotMatch" translate="global.messages.error.dontmatch">
The password and its confirmation do not match!
</div>
</div>
<div class="col-md-8 col-md-offset-2">
<form ng-show="!success" name="form" role="form" novalidate ng-submit="register()" show-validation>
<form ng-show="!vm.success" name="form" role="form" novalidate ng-submit="vm.register()" show-validation>
<div class="form-group">
<label class="control-label" for="login" translate="global.form.username">Username</label>
<input type="text" class="form-control" id="login" name="login" placeholder="{{'global.form.username.placeholder' | translate}}"
ng-model="registerAccount.login" ng-minlength=1 ng-maxlength=50 ng-pattern="/^[a-z0-9]*$/" required>
ng-model="vm.registerAccount.login" ng-minlength=1 ng-maxlength=50 ng-pattern="/^[a-z0-9]*$/" required>
<div ng-show="form.login.$dirty && form.login.$invalid">
<p class="help-block"
ng-show="form.login.$error.required" translate="register.messages.validate.login.required">
@ -52,7 +52,7 @@
<div class="form-group">
<label class="control-label" for="email" translate="global.form.email">E-mail</label>
<input type="email" class="form-control" id="email" name="email" placeholder="{{'global.form.email.placeholder' | translate}}"
ng-model="registerAccount.email" ng-minlength=5 ng-maxlength=100 required>
ng-model="vm.registerAccount.email" ng-minlength=5 ng-maxlength=100 required>
<div ng-show="form.email.$dirty && form.email.$invalid">
<p class="help-block"
ng-show="form.email.$error.required" translate="global.messages.validate.email.required">
@ -75,7 +75,7 @@
<div class="form-group">
<label class="control-label" for="password" translate="global.form.newpassword">New password</label>
<input type="password" class="form-control" id="password" name="password" placeholder="{{'global.form.newpassword.placeholder' | translate}}"
ng-model="registerAccount.password" ng-minlength=5 ng-maxlength=50 required>
ng-model="vm.registerAccount.password" ng-minlength=5 ng-maxlength=50 required>
<div ng-show="form.password.$dirty && form.password.$invalid">
<p class="help-block"
ng-show="form.password.$error.required" translate="global.messages.validate.newpassword.required">
@ -90,12 +90,12 @@
Your password cannot be longer than 50 characters.
</p>
</div>
<password-strength-bar password-to-check="registerAccount.password"></password-strength-bar>
<password-strength-bar password-to-check="vm.registerAccount.password"></password-strength-bar>
</div>
<div class="form-group">
<label class="control-label" for="confirmPassword" translate="global.form.confirmpassword">New password confirmation</label>
<input type="password" class="form-control" id="confirmPassword" name="confirmPassword" placeholder="{{'global.form.confirmpassword.placeholder' | translate}}"
ng-model="confirmPassword" ng-minlength=5 ng-maxlength=50 required>
ng-model="vm.confirmPassword" ng-minlength=5 ng-maxlength=50 required>
<div ng-show="form.confirmPassword.$dirty && form.confirmPassword.$invalid">
<p class="help-block"
ng-show="form.confirmPassword.$error.required" translate="global.messages.validate.confirmpassword.required">
@ -115,8 +115,8 @@
<button type="submit" ng-disabled="form.$invalid" class="btn btn-primary" translate="register.form.button">Register</button>
</form>
<p></p>
<div class="alert alert-warning" translate="global.messages.info.authenticated">
If you want to <a class="alert-link" href="#/login">sign in</a>, you can try the default accounts:<br/>- Administrator (login="admin" and password="admin") <br/>- User (login="user" and password="user").
<div class="alert alert-warning" translate="global.messages.info.authenticated" translate-compile>
If you want to <a class="alert-link" href="" ng-click="login()">sign in</a>, you can try the default accounts:<br/>- Administrator (login="admin" and password="admin") <br/>- User (login="user" and password="user").
</div>
</div>
</div>

View File

@ -0,0 +1,33 @@
(function() {
'use strict';
angular
.module('sampleApplicationApp')
.config(stateConfig);
stateConfig.$inject = ['$stateProvider'];
function stateConfig($stateProvider) {
$stateProvider.state('register', {
parent: 'account',
url: '/register',
data: {
authorities: [],
pageTitle: 'register.title'
},
views: {
'content@': {
templateUrl: 'app/account/register/register.html',
controller: 'RegisterController',
controllerAs: 'vm'
}
},
resolve: {
translatePartialLoader: ['$translate', '$translatePartialLoader', function ($translate, $translatePartialLoader) {
$translatePartialLoader.addPart('register');
return $translate.refresh();
}]
}
});
}
})();

View File

@ -0,0 +1,37 @@
(function() {
'use strict';
angular
.module('sampleApplicationApp')
.controller('ResetFinishController', ResetFinishController);
ResetFinishController.$inject = ['$stateParams', '$timeout', 'Auth', 'LoginService'];
function ResetFinishController ($stateParams, $timeout, Auth, LoginService) {
var vm = this;
vm.keyMissing = angular.isUndefined($stateParams.key);
vm.confirmPassword = null;
vm.doNotMatch = null;
vm.error = null;
vm.finishReset = finishReset;
vm.login = LoginService.open;
vm.resetAccount = {};
vm.success = null;
$timeout(function (){angular.element('[ng-model="vm.resetAccount.password"]').focus();});
function finishReset() {
if (vm.resetAccount.password !== vm.confirmPassword) {
vm.doNotMatch = 'ERROR';
} else {
Auth.resetPasswordFinish({key: $stateParams.key, newPassword: vm.resetAccount.password}).then(function () {
vm.success = 'OK';
}).catch(function () {
vm.success = null;
vm.error = 'ERROR';
});
}
}
}
})();

View File

@ -3,32 +3,32 @@
<div class="col-md-4 col-md-offset-4">
<h1 translate="reset.finish.title">Reset password</h1>
<div class="alert alert-danger" translate="reset.finish.messages.keymissing" ng-show="keyMissing">
<div class="alert alert-danger" translate="reset.finish.messages.keymissing" ng-show="vm.keyMissing">
<strong>The password reset key is missing.</strong>
</div>
<div class="alert alert-warning" ng-hide="success || keyMissing">
<div class="alert alert-warning" ng-hide="vm.success || vm.keyMissing">
<p translate="reset.finish.messages.info">Choose a new password</p>
</div>
<div class="alert alert-danger" ng-show="error">
<div class="alert alert-danger" ng-show="vm.error">
<p translate="reset.finish.messages.error">Your password couldn't be reset. Remember a password request is only valid for 24 hours.</p>
</div>
<div class="alert alert-success" ng-show="success">
<p translate="reset.finish.messages.success"><strong>Your password has been reset.</strong> Please <a class="alert-link" href="#/login">sign in</a>.</p>
<div class="alert alert-success" ng-show="vm.success">
<p translate="reset.finish.messages.success" translate-compile><strong>Your password has been reset.</strong> Please <a class="alert-link" href="" ng-click="login">sign in</a>.</p>
</div>
<div class="alert alert-danger" ng-show="doNotMatch" translate="global.messages.error.dontmatch">
<div class="alert alert-danger" ng-show="vm.doNotMatch" translate="global.messages.error.dontmatch">
The password and its confirmation do not match!
</div>
<div ng-hide="keyMissing">
<form ng-show="!success" name="form" role="form" novalidate ng-submit="finishReset()" show-validation>
<div ng-hide="vm.keyMissing">
<form ng-show="!vm.success" name="form" role="form" novalidate ng-submit="vm.finishReset()" show-validation>
<div class="form-group">
<label class="control-label" for="password" translate="global.form.newpassword">New password</label>
<input type="password" class="form-control" id="password" name="password" placeholder="{{'global.form.newpassword.placeholder' | translate}}"
ng-model="resetAccount.password" ng-minlength=5 ng-maxlength=50 required>
ng-model="vm.resetAccount.password" ng-minlength=5 ng-maxlength=50 required>
<div ng-show="form.password.$dirty && form.password.$invalid">
<p class="help-block"
ng-show="form.password.$error.required" translate="global.messages.validate.newpassword.required">
@ -43,13 +43,13 @@
Your password cannot be longer than 50 characters.
</p>
</div>
<password-strength-bar password-to-check="resetAccount.password"></password-strength-bar>
<password-strength-bar password-to-check="vm.resetAccount.password"></password-strength-bar>
</div>
<div class="form-group">
<label class="control-label" for="confirmPassword" translate="global.form.confirmpassword">New password confirmation</label>
<input type="password" class="form-control" id="confirmPassword" name="confirmPassword" placeholder="{{'global.form.confirmpassword.placeholder' | translate}}"
ng-model="confirmPassword" ng-minlength=5 ng-maxlength=50 required>
ng-model="vm.confirmPassword" ng-minlength=5 ng-maxlength=50 required>
<div ng-show="form.confirmPassword.$dirty && form.confirmPassword.$invalid">
<p class="help-block"
ng-show="form.confirmPassword.$error.required" translate="global.messages.validate.confirmpassword.required">

View File

@ -0,0 +1,32 @@
(function() {
'use strict';
angular
.module('sampleApplicationApp')
.config(stateConfig);
stateConfig.$inject = ['$stateProvider'];
function stateConfig($stateProvider) {
$stateProvider.state('finishReset', {
parent: 'account',
url: '/reset/finish?key',
data: {
authorities: []
},
views: {
'content@': {
templateUrl: 'app/account/reset/finish/reset.finish.html',
controller: 'ResetFinishController',
controllerAs: 'vm'
}
},
resolve: {
translatePartialLoader: ['$translate', '$translatePartialLoader', function ($translate, $translatePartialLoader) {
$translatePartialLoader.addPart('reset');
return $translate.refresh();
}]
}
});
}
})();

View File

@ -0,0 +1,38 @@
(function() {
'use strict';
angular
.module('sampleApplicationApp')
.controller('RequestResetController', RequestResetController);
RequestResetController.$inject = ['$timeout', 'Auth'];
function RequestResetController ($timeout, Auth) {
var vm = this;
vm.error = null;
vm.errorEmailNotExists = null;
vm.requestReset = requestReset;
vm.resetAccount = {};
vm.success = null;
$timeout(function (){angular.element('[ng-model="resetAccount.email"]').focus();});
function requestReset () {
vm.error = null;
vm.errorEmailNotExists = null;
Auth.resetPasswordInit(vm.resetAccount.email).then(function () {
vm.success = 'OK';
}).catch(function (response) {
vm.success = null;
if (response.status === 400 && response.data === 'e-mail address not registered') {
vm.errorEmailNotExists = 'ERROR';
} else {
vm.error = 'ERROR';
}
});
}
}
})();

View File

@ -3,23 +3,23 @@
<div class="col-md-8 col-md-offset-2">
<h1 translate="reset.request.title">Reset your password</h1>
<div class="alert alert-danger" translate="reset.request.messages.notfound" ng-show="errorEmailNotExists">
<div class="alert alert-danger" translate="reset.request.messages.notfound" ng-show="vm.errorEmailNotExists">
<strong>E-Mail address isn't registered!</strong> Please check and try again.
</div>
<div class="alert alert-warning" ng-hide="success">
<div class="alert alert-warning" ng-hide="vm.success">
<p translate="reset.request.messages.info">Enter the e-mail address you used to register.</p>
</div>
<div class="alert alert-success" ng-show="success == 'OK'">
<div class="alert alert-success" ng-show="vm.success == 'OK'">
<p translate="reset.request.messages.success">Check your e-mails for details on how to reset your password.</p>
</div>
<form ng-show="!success" name="form" role="form" novalidate ng-submit="requestReset()" show-validation>
<form ng-show="!vm.success" name="form" role="form" novalidate ng-submit="vm.requestReset()" show-validation>
<div class="form-group">
<label class="control-label" for="email" translate="global.form.email">E-mail</label>
<input type="email" class="form-control" id="email" name="email" placeholder="{{'global.form.email.placeholder' | translate}}"
ng-model="resetAccount.email" ng-minlength=5 ng-maxlength=100 required>
ng-model="vm.resetAccount.email" ng-minlength=5 ng-maxlength=100 required>
<div ng-show="form.email.$dirty && form.email.$invalid">
<p class="help-block"
ng-show="form.email.$error.required" translate="global.messages.validate.email.required">

View File

@ -0,0 +1,32 @@
(function() {
'use strict';
angular
.module('sampleApplicationApp')
.config(stateConfig);
stateConfig.$inject = ['$stateProvider'];
function stateConfig($stateProvider) {
$stateProvider.state('requestReset', {
parent: 'account',
url: '/reset/request',
data: {
authorities: []
},
views: {
'content@': {
templateUrl: 'app/account/reset/request/reset.request.html',
controller: 'RequestResetController',
controllerAs: 'vm'
}
},
resolve: {
translatePartialLoader: ['$translate', '$translatePartialLoader', function ($translate, $translatePartialLoader) {
$translatePartialLoader.addPart('reset');
return $translate.refresh();
}]
}
});
}
})();

View File

@ -0,0 +1,37 @@
(function() {
'use strict';
angular
.module('sampleApplicationApp')
.controller('SessionsController', SessionsController);
SessionsController.$inject = ['Sessions', 'Principal'];
function SessionsController (Sessions, Principal) {
var vm = this;
vm.account = null;
vm.error = null;
vm.invalidate = invalidate;
vm.sessions = Sessions.getAll();
vm.success = null;
Principal.identity().then(function(account) {
vm.account = account;
});
function invalidate (series) {
Sessions.delete({series: encodeURIComponent(series)},
function () {
vm.error = null;
vm.success = 'OK';
vm.sessions = Sessions.getAll();
},
function () {
vm.success = null;
vm.error = 'ERROR';
});
}
}
})();

View File

@ -1,11 +1,11 @@
<div>
<h2 translate="sessions.title" translate-values="{username: '{{account.login}}'}">Active sessions for [<b>{{account.login}}</b>]</h2>
<h2 translate="sessions.title" translate-values="{username: '{{vm.account.login}}'}">Active sessions for [<b>{{vm.account.login}}</b>]</h2>
<div class="alert alert-success" ng-show="success" translate="sessions.messages.success">
<div class="alert alert-success" ng-show="vm.success" translate="sessions.messages.success">
<strong>Session invalidated!</strong>
</div>
<div class="alert alert-danger" ng-show="error" translate="sessions.messages.error">
<div class="alert alert-danger" ng-show="vm.error" translate="sessions.messages.error">
<strong>An error has occured!</strong> The session could not be invalidated.
</div>
@ -20,14 +20,14 @@
</tr>
</thead>
<tbody>
<tr ng-repeat="session in sessions">
<tr ng-repeat="session in vm.sessions">
<td>{{session.ipAddress}}</td>
<td>{{session.userAgent}}</td>
<td>{{session.formattedTokenDate}}</td>
<td>
<button type="submit"
class="btn btn-primary"
ng-click="invalidate(session.series)" translate="sessions.table.button">
ng-click="vm.invalidate(session.series)" translate="sessions.table.button">
Invalidate
</button>
</td>

View File

@ -0,0 +1,33 @@
(function() {
'use strict';
angular
.module('sampleApplicationApp')
.config(stateConfig);
stateConfig.$inject = ['$stateProvider'];
function stateConfig($stateProvider) {
$stateProvider.state('sessions', {
parent: 'account',
url: '/sessions',
data: {
authorities: ['ROLE_USER'],
pageTitle: 'global.menu.account.sessions'
},
views: {
'content@': {
templateUrl: 'app/account/sessions/sessions.html',
controller: 'SessionsController',
controllerAs: 'vm'
}
},
resolve: {
translatePartialLoader: ['$translate', '$translatePartialLoader', function ($translate, $translatePartialLoader) {
$translatePartialLoader.addPart('sessions');
return $translate.refresh();
}]
}
});
}
})();

Some files were not shown because too many files have changed in this diff Show More