Initial version of isdashboard generated by generator-jhipster@8.0.0-beta.2

This commit is contained in:
Maria Teresa Paratore 2023-08-02 11:18:57 +02:00
commit abca9cf59e
237 changed files with 15285 additions and 0 deletions

25
.devcontainer/Dockerfile Normal file
View File

@ -0,0 +1,25 @@
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.209.6/containers/java/.devcontainer/base.Dockerfile
# [Choice] Java version (use -bullseye variants on local arm64/Apple Silicon): 17, 17-bullseye, 17-buster
ARG VARIANT="17"
FROM mcr.microsoft.com/vscode/devcontainers/java:0-${VARIANT}
# [Option] Install Maven
ARG INSTALL_MAVEN="false"
ARG MAVEN_VERSION=""
# [Option] Install Gradle
ARG INSTALL_GRADLE="false"
ARG GRADLE_VERSION=""
RUN if [ "${INSTALL_MAVEN}" = "true" ]; then su vscode -c "umask 0002 && . /usr/local/sdkman/bin/sdkman-init.sh && sdk install maven \"${MAVEN_VERSION}\""; fi \
&& if [ "${INSTALL_GRADLE}" = "true" ]; then su vscode -c "umask 0002 && . /usr/local/sdkman/bin/sdkman-init.sh && sdk install gradle \"${GRADLE_VERSION}\""; fi
# [Choice] Node.js version: none, lts/*, 16, 14, 12, 10
ARG NODE_VERSION="none"
RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi
# [Optional] Uncomment this section to install additional OS packages.
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
# && apt-get -y install --no-install-recommends <your-package-list-here>
# [Optional] Uncomment this line to install global node packages.
# RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g <your-package-here>" 2>&1

View File

@ -0,0 +1,53 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.209.6/containers/java
{
"name": "Isdashboard",
"build": {
"dockerfile": "Dockerfile",
"args": {
// Update the VARIANT arg to pick a Java version: 17, 19
// Append -bullseye or -buster to pin to an OS version.
// Use the -bullseye variants on local arm64/Apple Silicon.
"VARIANT": "17-bullseye",
// Options
// maven and gradle wrappers are used by default, we don't need them installed globally
// "INSTALL_MAVEN": "true",
// "INSTALL_GRADLE": "false",
"NODE_VERSION": "18.16.1"
}
},
"customizations": {
"vscode": {
// Set *default* container specific settings.json values on container create.
"settings": {
"java.jdt.ls.java.home": "/docker-java-home"
},
// Add the IDs of extensions you want installed when the container is created.
"extensions": [
"angular.ng-template",
"christian-kohler.npm-intellisense",
"firsttris.vscode-jest-runner",
"ms-vscode.vscode-typescript-tslint-plugin",
"dbaeumer.vscode-eslint",
"vscjava.vscode-java-pack",
"pivotal.vscode-boot-dev-pack",
"esbenp.prettier-vscode"
]
}
},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [4200, 3001, 9000, 8080],
// Use 'postCreateCommand' to run commands after the container is created.
// "postCreateCommand": "java -version",
// Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
"remoteUser": "vscode",
"features": {
"docker-in-docker": "latest",
"docker-from-docker": "latest"
}
}

23
.editorconfig Normal file
View File

@ -0,0 +1,23 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
# We recommend you to keep these unchanged
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
# Change these settings to your own preference
indent_style = space
indent_size = 4
[*.{ts,tsx,js,jsx,json,css,scss,yml,html,vue}]
indent_size = 2
[*.md]
trim_trailing_whitespace = false

9
.eslintignore Normal file
View File

@ -0,0 +1,9 @@
node_modules/
src/main/docker/
jest.conf.js
webpack/
target/
build/
node/
coverage/
postcss.config.js

99
.eslintrc.json Normal file
View File

@ -0,0 +1,99 @@
{
"parser": "@typescript-eslint/parser",
"plugins": ["@angular-eslint/eslint-plugin", "@typescript-eslint"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking",
"plugin:@angular-eslint/recommended",
"prettier",
"eslint-config-prettier"
],
"env": {
"browser": true,
"es6": true,
"commonjs": true
},
"parserOptions": {
"ecmaVersion": 2018,
"sourceType": "module",
"project": ["./tsconfig.app.json", "./tsconfig.spec.json"]
},
"rules": {
"@angular-eslint/component-selector": [
"error",
{
"type": "element",
"prefix": "jhi",
"style": "kebab-case"
}
],
"@angular-eslint/directive-selector": [
"error",
{
"type": "attribute",
"prefix": "jhi",
"style": "camelCase"
}
],
"@angular-eslint/relative-url-prefix": "error",
"@typescript-eslint/ban-types": [
"error",
{
"extendDefaults": true,
"types": {
"{}": false
}
}
],
"@typescript-eslint/explicit-function-return-type": ["error", { "allowExpressions": true }],
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/member-ordering": [
"error",
{
"default": [
"public-static-field",
"protected-static-field",
"private-static-field",
"public-instance-field",
"protected-instance-field",
"private-instance-field",
"constructor",
"public-static-method",
"protected-static-method",
"private-static-method",
"public-instance-method",
"protected-instance-method",
"private-instance-method"
]
}
],
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-floating-promises": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/no-parameter-properties": ["warn", { "allows": ["public", "private", "protected"] }],
"@typescript-eslint/no-shadow": ["error"],
"@typescript-eslint/no-unnecessary-condition": "error",
"@typescript-eslint/no-unsafe-argument": "off",
"@typescript-eslint/no-unsafe-assignment": "off",
"@typescript-eslint/no-unsafe-call": "off",
"@typescript-eslint/no-unsafe-member-access": "off",
"@typescript-eslint/prefer-nullish-coalescing": "error",
"@typescript-eslint/prefer-optional-chain": "error",
"@typescript-eslint/unbound-method": "off",
"arrow-body-style": "error",
"curly": "error",
"eqeqeq": ["error", "always", { "null": "ignore" }],
"guard-for-in": "error",
"no-bitwise": "error",
"no-caller": "error",
"no-console": ["error", { "allow": ["warn", "error"] }],
"no-eval": "error",
"no-labels": "error",
"no-new": "error",
"no-new-wrappers": "error",
"object-shorthand": ["error", "always", { "avoidExplicitReturnArrows": true }],
"radix": "error",
"spaced-comment": ["warn", "always"]
}
}

150
.gitattributes vendored Normal file
View File

@ -0,0 +1,150 @@
# This file is inspired by https://github.com/alexkaratarakis/gitattributes
#
# Auto detect text files and perform LF normalization
# http://davidlaing.com/2012/09/19/customise-your-gitattributes-to-become-a-git-ninja/
* text=auto
# The above will handle all files NOT found below
# These files are text and should be normalized (Convert crlf => lf)
*.bat text eol=crlf
*.cmd text eol=crlf
*.ps1 text eol=crlf
*.coffee text
*.css text
*.cql text
*.df text
*.ejs text
*.html text
*.java text
*.js text
*.json text
*.less text
*.properties text
*.sass text
*.scss text
*.sh text eol=lf
*.sql text
*.txt text
*.ts text
*.xml text
*.yaml text
*.yml text
# Documents
*.doc diff=astextplain
*.DOC diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot diff=astextplain
*.DOT diff=astextplain
*.pdf diff=astextplain
*.PDF diff=astextplain
*.rtf diff=astextplain
*.RTF diff=astextplain
*.markdown text
*.md text
*.adoc text
*.textile text
*.mustache text
*.csv text
*.tab text
*.tsv text
*.txt text
AUTHORS text
CHANGELOG text
CHANGES text
CONTRIBUTING text
COPYING text
copyright text
*COPYRIGHT* text
INSTALL text
license text
LICENSE text
NEWS text
readme text
*README* text
TODO text
# Graphics
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.tif binary
*.tiff binary
*.ico binary
# SVG treated as an asset (binary) by default. If you want to treat it as text,
# comment-out the following line and uncomment the line after.
*.svg binary
#*.svg text
*.eps binary
# These files are binary and should be left untouched
# (binary is a macro for -text -diff)
*.class binary
*.jar binary
*.war binary
## LINTERS
.csslintrc text
.eslintrc text
.jscsrc text
.jshintrc text
.jshintignore text
.stylelintrc text
## CONFIGS
*.conf text
*.config text
.editorconfig text
.gitattributes text
.gitconfig text
.gitignore text
.htaccess text
*.npmignore text
## HEROKU
Procfile text
.slugignore text
## AUDIO
*.kar binary
*.m4a binary
*.mid binary
*.midi binary
*.mp3 binary
*.ogg binary
*.ra binary
## VIDEO
*.3gpp binary
*.3gp binary
*.as binary
*.asf binary
*.asx binary
*.fla binary
*.flv binary
*.m4v binary
*.mng binary
*.mov binary
*.mp4 binary
*.mpeg binary
*.mpg binary
*.swc binary
*.swf binary
*.webm binary
## ARCHIVES
*.7z binary
*.gz binary
*.rar binary
*.tar binary
*.zip binary
## FONTS
*.ttf binary
*.eot binary
*.otf binary
*.woff binary
*.woff2 binary

151
.gitignore vendored Normal file
View File

@ -0,0 +1,151 @@
######################
# Node
######################
/node/
node_tmp/
node_modules/
npm-debug.log.*
/.awcache/*
/.cache-loader/*
######################
# SASS
######################
.sass-cache/
######################
# Eclipse
######################
*.pydevproject
.project
.metadata
tmp/
tmp/**/*
*.tmp
*.bak
*.swp
*~.nib
local.properties
.classpath
.settings/
.loadpath
.factorypath
# External tool builders
.externalToolBuilders/**
# Locally stored "Eclipse launch configurations"
*.launch
# CDT-specific
.cproject
# PDT-specific
.buildpath
# STS-specific
/.sts4-cache/*
######################
# IntelliJ
######################
.idea/
*.iml
*.iws
*.ipr
*.ids
*.orig
classes/
out/
######################
# Visual Studio Code
######################
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
######################
# Maven
######################
/log/
/target/
######################
# Gradle
######################
.gradle/
/build/
######################
# Package Files
######################
*.jar
*.war
*.ear
*.db
######################
# Windows
######################
# Windows image file caches
Thumbs.db
# Folder config file
Desktop.ini
######################
# Mac OSX
######################
.DS_Store
.svn
# Thumbnails
._*
# Files that might appear on external disk
.Spotlight-V100
.Trashes
######################
# Directories
######################
/bin/
/deploy/
######################
# Logs
######################
*.log*
######################
# Others
######################
*.class
*.*~
*~
.merge_file*
######################
# Gradle Wrapper
######################
!gradle/wrapper/gradle-wrapper.jar
######################
# Maven Wrapper
######################
!.mvn/wrapper/maven-wrapper.jar
######################
# ESLint
######################
.eslintcache
######################
# Code coverage
######################
/coverage/
/.nyc_output/

5
.husky/pre-commit Normal file
View File

@ -0,0 +1,5 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
"$(dirname "$0")/../npmw" exec --no-install lint-staged

3
.lintstagedrc.js Normal file
View File

@ -0,0 +1,3 @@
module.exports = {
'{,src/**/,webpack/}*.{md,json,yml,html,cjs,mjs,js,ts,tsx,css,scss,java}': ['prettier --write'],
};

1
.mvn/jvm.config Normal file
View File

@ -0,0 +1 @@

BIN
.mvn/wrapper/maven-wrapper.jar vendored Normal file

Binary file not shown.

18
.mvn/wrapper/maven-wrapper.properties vendored Normal file
View File

@ -0,0 +1,18 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.2/apache-maven-3.9.2-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar

8
.prettierignore Normal file
View File

@ -0,0 +1,8 @@
node_modules
target
build
package-lock.json
.git
.mvn
gradle
.gradle

18
.prettierrc Normal file
View File

@ -0,0 +1,18 @@
# Prettier configuration
printWidth: 140
singleQuote: true
tabWidth: 2
useTabs: false
# js and ts rules:
arrowParens: avoid
# jsx and tsx rules:
bracketSameLine: false
# java rules:
overrides:
- files: "*.java"
options:
tabWidth: 4

46
.yo-rc.json Normal file
View File

@ -0,0 +1,46 @@
{
"generator-jhipster": {
"applicationType": "monolith",
"authenticationType": "session",
"baseName": "isdashboard",
"buildTool": "maven",
"cacheProvider": "ehcache",
"clientFramework": "angular",
"clientTheme": "none",
"creationTimestamp": 1690967932750,
"databaseType": "no",
"devDatabaseType": "no",
"devServerPort": 4200,
"dtoSuffix": "DTO",
"enableGradleEnterprise": null,
"enableHibernateCache": false,
"enableSwaggerCodegen": false,
"enableTranslation": false,
"entities": [],
"entitySuffix": "",
"gradleEnterpriseHost": null,
"jhiPrefix": "jhi",
"jhipsterVersion": "8.0.0-beta.2",
"messageBroker": false,
"microfrontend": false,
"microfrontends": [],
"nativeLanguage": "en",
"packageFolder": "org/gcube/isdashboard",
"packageName": "org.gcube.isdashboard",
"pages": [],
"prodDatabaseType": "no",
"reactive": false,
"rememberMeKey": "6038bcb93c3f8cc61c163e71aa55573477e46c3535cf0624212567f396b89e7be83d90b03885a818d14b1b7f4928ab548561",
"searchEngine": false,
"serverPort": null,
"serverSideOptions": [],
"serviceDiscoveryType": false,
"skipCheckLengthOfIdentifier": false,
"skipClient": false,
"skipFakeData": false,
"skipUserManagement": true,
"testFrameworks": [],
"websocket": false,
"withAdminUi": false
}
}

246
README.md Normal file
View File

@ -0,0 +1,246 @@
# isdashboard
This application was generated using JHipster 8.0.0-beta.2, you can find documentation and help at [https://www.jhipster.tech/documentation-archive/v8.0.0-beta.2](https://www.jhipster.tech/documentation-archive/v8.0.0-beta.2).
## Project Structure
Node is required for generation and recommended for development. `package.json` is always generated for a better development experience with prettier, commit hooks, scripts and so on.
In the project root, JHipster generates configuration files for tools like git, prettier, eslint, husky, and others that are well known and you can find references in the web.
`/src/*` structure follows default Java structure.
- `.yo-rc.json` - Yeoman configuration file
JHipster configuration is stored in this file at `generator-jhipster` key. You may find `generator-jhipster-*` for specific blueprints configuration.
- `.yo-resolve` (optional) - Yeoman conflict resolver
Allows to use a specific action when conflicts are found skipping prompts for files that matches a pattern. Each line should match `[pattern] [action]` with pattern been a [Minimatch](https://github.com/isaacs/minimatch#minimatch) pattern and action been one of skip (default if ommited) or force. Lines starting with `#` are considered comments and are ignored.
- `.jhipster/*.json` - JHipster entity configuration files
- `npmw` - wrapper to use locally installed npm.
JHipster installs Node and npm locally using the build tool by default. This wrapper makes sure npm is installed locally and uses it avoiding some differences different versions can cause. By using `./npmw` instead of the traditional `npm` you can configure a Node-less environment to develop or test your application.
- `/src/main/docker` - Docker configurations for the application and services that the application depends on
## 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.
Depending on your system, you can install Node either from source or as a pre-packaged bundle.
After installing Node, you should be able to run the following command to install development tools.
You will only need to run this command when dependencies change in [package.json](package.json).
```
npm install
```
We use npm scripts and [Angular CLI][] with [Webpack][] as our build system.
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.
```
./mvnw
npm start
```
Npm is also used to manage CSS and JavaScript dependencies used in this application. You can upgrade dependencies by
specifying a newer version in [package.json](package.json). You can also run `npm update` and `npm install` to manage dependencies.
Add the `help` flag on any command to see how you can use it. For example, `npm help update`.
The `npm run` command will list all of the scripts available to run for this project.
### PWA Support
JHipster ships with PWA (Progressive Web App) support, and it's turned off by default. One of the main components of a PWA is a service worker.
The service worker initialization code is disabled by default. To enable it, uncomment the following code in `src/main/webapp/app/app.module.ts`:
```typescript
ServiceWorkerModule.register('ngsw-worker.js', { enabled: false }),
```
### Managing dependencies
For example, to add [Leaflet][] library as a runtime dependency of your application, you would run following command:
```
npm install --save --save-exact leaflet
```
To benefit from TypeScript type definitions from [DefinitelyTyped][] repository in development, you would run following command:
```
npm install --save-dev --save-exact @types/leaflet
```
Then you would import the JS and CSS files specified in library's installation instructions so that [Webpack][] knows about them:
Edit [src/main/webapp/app/app.module.ts](src/main/webapp/app/app.module.ts) file:
```
import 'leaflet/dist/leaflet.js';
```
Edit [src/main/webapp/content/scss/vendor.scss](src/main/webapp/content/scss/vendor.scss) file:
```
@import 'leaflet/dist/leaflet.css';
```
Note: There are still a few other things remaining to do for Leaflet that we won't detail here.
For further instructions on how to develop with JHipster, have a look at [Using JHipster in development][].
### Using Angular CLI
You can also use [Angular CLI][] to generate some custom client code.
For example, the following command:
```
ng generate component my-component
```
will generate few files:
```
create src/main/webapp/app/my-component/my-component.component.html
create src/main/webapp/app/my-component/my-component.component.ts
update src/main/webapp/app/app.module.ts
```
### JHipster Control Center
JHipster Control Center can help you manage and control your application(s). You can start a local control center server (accessible on http://localhost:7419) with:
```
docker compose -f src/main/docker/jhipster-control-center.yml up
```
## Building for production
### Packaging as jar
To build the final jar and optimize the isdashboard application for production, run:
```
./mvnw -Pprod clean verify
```
This will concatenate and minify the client CSS and JavaScript files. It will also modify `index.html` so it references these new files.
To ensure everything worked, run:
```
java -jar target/*.jar
```
Then navigate to [http://localhost:8080](http://localhost:8080) in your browser.
Refer to [Using JHipster in production][] for more details.
### Packaging as war
To package your application as a war in order to deploy it to an application server, run:
```
./mvnw -Pprod,war clean verify
```
## Testing
To launch your application's tests, run:
```
./mvnw verify
```
### Client tests
Unit tests are run by [Jest][]. They're located in [src/test/javascript/](src/test/javascript/) and can be run with:
```
npm test
```
For more information, refer to the [Running tests page][].
### Code quality
Sonar is used to analyse code quality. You can start a local Sonar server (accessible on http://localhost:9001) with:
```
docker compose -f src/main/docker/sonar.yml up -d
```
Note: we have turned off forced authentication redirect for UI in [src/main/docker/sonar.yml](src/main/docker/sonar.yml) for out of the box experience while trying out SonarQube, for real use cases turn it back on.
You can run a Sonar analysis with using the [sonar-scanner](https://docs.sonarqube.org/display/SCAN/Analyzing+with+SonarQube+Scanner) or by using the maven plugin.
Then, run a Sonar analysis:
```
./mvnw -Pprod clean verify sonar:sonar -Dsonar.login=admin -Dsonar.password=admin
```
If you need to re-run the Sonar phase, please be sure to specify at least the `initialize` phase since Sonar properties are loaded from the sonar-project.properties file.
```
./mvnw initialize sonar:sonar -Dsonar.login=admin -Dsonar.password=admin
```
Additionally, Instead of passing `sonar.password` and `sonar.login` as CLI arguments, these parameters can be configured from [sonar-project.properties](sonar-project.properties) as shown below:
```
sonar.login=admin
sonar.password=admin
```
For more information, refer to the [Code quality page][].
## Using Docker to simplify development (optional)
You can use Docker to improve your JHipster development experience. A number of docker-compose configuration are available in the [src/main/docker](src/main/docker) folder to launch required third party services.
You can also fully dockerize your application and all the services that it depends on.
To achieve this, first build a docker image of your app by running:
```
npm run java:docker
```
Or build a arm64 docker image when using an arm64 processor os like MacOS with M1 processor family running:
```
npm run java:docker:arm64
```
Then run:
```
docker compose -f src/main/docker/app.yml up -d
```
When running Docker Desktop on MacOS Big Sur or later, consider enabling experimental `Use the new Virtualization framework` for better processing performance ([disk access performance is worse](https://github.com/docker/roadmap/issues/7)).
For more information refer to [Using Docker and Docker-Compose][], this page also contains information on the docker-compose sub-generator (`jhipster docker-compose`), which is able to generate docker configurations for one or several JHipster applications.
## Continuous Integration (optional)
To configure CI for your project, run the ci-cd sub-generator (`jhipster ci-cd`), this will let you generate configuration files for a number of Continuous Integration systems. Consult the [Setting up Continuous Integration][] page for more information.
[JHipster Homepage and latest documentation]: https://www.jhipster.tech
[JHipster 8.0.0-beta.2 archive]: https://www.jhipster.tech/documentation-archive/v8.0.0-beta.2
[Using JHipster in development]: https://www.jhipster.tech/documentation-archive/v8.0.0-beta.2/development/
[Using Docker and Docker-Compose]: https://www.jhipster.tech/documentation-archive/v8.0.0-beta.2/docker-compose
[Using JHipster in production]: https://www.jhipster.tech/documentation-archive/v8.0.0-beta.2/production/
[Running tests page]: https://www.jhipster.tech/documentation-archive/v8.0.0-beta.2/running-tests/
[Code quality page]: https://www.jhipster.tech/documentation-archive/v8.0.0-beta.2/code-quality/
[Setting up Continuous Integration]: https://www.jhipster.tech/documentation-archive/v8.0.0-beta.2/setting-up-ci/
[Node.js]: https://nodejs.org/
[NPM]: https://www.npmjs.com/
[Webpack]: https://webpack.github.io/
[BrowserSync]: https://www.browsersync.io/
[Jest]: https://facebook.github.io/jest/
[Leaflet]: https://leafletjs.com/
[DefinitelyTyped]: https://definitelytyped.org/
[Angular CLI]: https://cli.angular.io/

109
angular.json Normal file
View File

@ -0,0 +1,109 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"isdashboard": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"style": "scss"
},
"@schematics/angular:application": {
"strict": true
}
},
"root": "",
"sourceRoot": "src/main/webapp",
"prefix": "jhi",
"architect": {
"build": {
"builder": "@angular-builders/custom-webpack:browser",
"options": {
"customWebpackConfig": {
"path": "./webpack/webpack.custom.js"
},
"outputPath": "target/classes/static/",
"index": "src/main/webapp/index.html",
"main": "src/main/webapp/main.ts",
"polyfills": ["zone.js"],
"tsConfig": "tsconfig.app.json",
"inlineStyleLanguage": "scss",
"assets": [
"src/main/webapp/content",
"src/main/webapp/favicon.ico",
"src/main/webapp/manifest.webapp",
"src/main/webapp/robots.txt"
],
"styles": ["src/main/webapp/content/scss/vendor.scss", "src/main/webapp/content/scss/global.scss"],
"scripts": []
},
"configurations": {
"production": {
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"serviceWorker": true,
"ngswConfigPath": "ngsw-config.json",
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
]
},
"development": {
"buildOptimizer": false,
"optimization": false,
"vendorChunk": true,
"extractLicenses": false,
"sourceMap": true,
"namedChunks": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-builders/custom-webpack:dev-server",
"options": {
"browserTarget": "isdashboard:build:development",
"port": 4200
},
"configurations": {
"production": {
"browserTarget": "isdashboard:build:production"
},
"development": {
"browserTarget": "isdashboard:build:development"
}
},
"defaultConfiguration": "development"
},
"test": {
"builder": "@angular-builders/jest:run",
"options": {
"configPath": "jest.conf.js"
}
}
}
}
},
"cli": {
"cache": {
"enabled": true,
"path": "./target/angular/",
"environment": "all"
},
"packageManager": "npm"
}
}

20
checkstyle.xml Normal file
View File

@ -0,0 +1,20 @@
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
"https://checkstyle.org/dtds/configuration_1_3.dtd">
<module name="Checker">
<!-- Configure checker to use UTF-8 encoding -->
<property name="charset" value="UTF-8"/>
<!-- Configure checker to run on files with these extensions -->
<property name="fileExtensions" value=""/>
<!-- For detailed checkstyle configuration, see https://github.com/spring-io/nohttp/tree/master/nohttp-checkstyle -->
<module name="io.spring.nohttp.checkstyle.check.NoHttpCheck">
<property name="allowlist" value="^\Qhttp://maven.apache.org/POM/4.0.0&#10;^\Qhttp://www.w3.org/2001/XMLSchema-instance"/>
</module>
<!-- Allow suppression with comments
// CHECKSTYLE:OFF
... ignored content ...
// CHECKSTYLE:ON
-->
<module name="SuppressWithPlainTextCommentFilter"/>
</module>

29
jest.conf.js Normal file
View File

@ -0,0 +1,29 @@
const { pathsToModuleNameMapper } = require('ts-jest');
const {
compilerOptions: { paths = {}, baseUrl = './' },
} = require('./tsconfig.json');
const environment = require('./webpack/environment');
module.exports = {
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$|dayjs/esm)'],
resolver: 'jest-preset-angular/build/resolvers/ng-jest-resolver.js',
globals: {
...environment,
},
roots: ['<rootDir>', `<rootDir>/${baseUrl}`],
modulePaths: [`<rootDir>/${baseUrl}`],
setupFiles: ['jest-date-mock'],
cacheDirectory: '<rootDir>/target/jest-cache',
coverageDirectory: '<rootDir>/target/test-results/',
moduleNameMapper: pathsToModuleNameMapper(paths, { prefix: `<rootDir>/${baseUrl}/` }),
reporters: [
'default',
['jest-junit', { outputDirectory: '<rootDir>/target/test-results/', outputName: 'TESTS-results-jest.xml' }],
['jest-sonar', { outputDirectory: './target/test-results/jest', outputName: 'TESTS-results-sonar.xml' }],
],
testMatch: ['<rootDir>/src/main/webapp/app/**/@(*.)@(spec.ts)'],
testEnvironmentOptions: {
url: 'https://jhipster.tech',
},
};

308
mvnw vendored Normal file
View File

@ -0,0 +1,308 @@
#!/bin/sh
# ----------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Apache Maven Wrapper startup batch script, version 3.2.0
#
# Required ENV vars:
# ------------------
# JAVA_HOME - location of a JDK home dir
#
# Optional ENV vars
# -----------------
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
# e.g. to debug Maven itself, use
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
# ----------------------------------------------------------------------------
if [ -z "$MAVEN_SKIP_RC" ] ; then
if [ -f /usr/local/etc/mavenrc ] ; then
. /usr/local/etc/mavenrc
fi
if [ -f /etc/mavenrc ] ; then
. /etc/mavenrc
fi
if [ -f "$HOME/.mavenrc" ] ; then
. "$HOME/.mavenrc"
fi
fi
# OS specific support. $var _must_ be set to either true or false.
cygwin=false;
darwin=false;
mingw=false
case "$(uname)" in
CYGWIN*) cygwin=true ;;
MINGW*) mingw=true;;
Darwin*) darwin=true
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
if [ -z "$JAVA_HOME" ]; then
if [ -x "/usr/libexec/java_home" ]; then
JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME
else
JAVA_HOME="/Library/Java/Home"; export JAVA_HOME
fi
fi
;;
esac
if [ -z "$JAVA_HOME" ] ; then
if [ -r /etc/gentoo-release ] ; then
JAVA_HOME=$(java-config --jre-home)
fi
fi
# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin ; then
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=$(cygpath --unix "$JAVA_HOME")
[ -n "$CLASSPATH" ] &&
CLASSPATH=$(cygpath --path --unix "$CLASSPATH")
fi
# For Mingw, ensure paths are in UNIX format before anything is touched
if $mingw ; then
[ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] &&
JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)"
fi
if [ -z "$JAVA_HOME" ]; then
javaExecutable="$(which javac)"
if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then
# readlink(1) is not available as standard on Solaris 10.
readLink=$(which readlink)
if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then
if $darwin ; then
javaHome="$(dirname "\"$javaExecutable\"")"
javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac"
else
javaExecutable="$(readlink -f "\"$javaExecutable\"")"
fi
javaHome="$(dirname "\"$javaExecutable\"")"
javaHome=$(expr "$javaHome" : '\(.*\)/bin')
JAVA_HOME="$javaHome"
export JAVA_HOME
fi
fi
fi
if [ -z "$JAVACMD" ] ; then
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
else
JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)"
fi
fi
if [ ! -x "$JAVACMD" ] ; then
echo "Error: JAVA_HOME is not defined correctly." >&2
echo " We cannot execute $JAVACMD" >&2
exit 1
fi
if [ -z "$JAVA_HOME" ] ; then
echo "Warning: JAVA_HOME environment variable is not set."
fi
# traverses directory structure from process work directory to filesystem root
# first directory with .mvn subdirectory is considered project base directory
find_maven_basedir() {
if [ -z "$1" ]
then
echo "Path not specified to find_maven_basedir"
return 1
fi
basedir="$1"
wdir="$1"
while [ "$wdir" != '/' ] ; do
if [ -d "$wdir"/.mvn ] ; then
basedir=$wdir
break
fi
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
if [ -d "${wdir}" ]; then
wdir=$(cd "$wdir/.." || exit 1; pwd)
fi
# end of workaround
done
printf '%s' "$(cd "$basedir" || exit 1; pwd)"
}
# concatenates all lines of a file
concat_lines() {
if [ -f "$1" ]; then
# Remove \r in case we run on Windows within Git Bash
# and check out the repository with auto CRLF management
# enabled. Otherwise, we may read lines that are delimited with
# \r\n and produce $'-Xarg\r' rather than -Xarg due to word
# splitting rules.
tr -s '\r\n' ' ' < "$1"
fi
}
log() {
if [ "$MVNW_VERBOSE" = true ]; then
printf '%s\n' "$1"
fi
}
BASE_DIR=$(find_maven_basedir "$(dirname "$0")")
if [ -z "$BASE_DIR" ]; then
exit 1;
fi
MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR
log "$MAVEN_PROJECTBASEDIR"
##########################################################################################
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
# This allows using the maven wrapper in projects that prohibit checking in binary data.
##########################################################################################
wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar"
if [ -r "$wrapperJarPath" ]; then
log "Found $wrapperJarPath"
else
log "Couldn't find $wrapperJarPath, downloading it ..."
if [ -n "$MVNW_REPOURL" ]; then
wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
else
wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
fi
while IFS="=" read -r key value; do
# Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' )
safeValue=$(echo "$value" | tr -d '\r')
case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;;
esac
done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
log "Downloading from: $wrapperUrl"
if $cygwin; then
wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath")
fi
if command -v wget > /dev/null; then
log "Found wget ... using wget"
[ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet"
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
else
wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
fi
elif command -v curl > /dev/null; then
log "Found curl ... using curl"
[ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent"
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
else
curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
fi
else
log "Falling back to using Java to download"
javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java"
javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class"
# For Cygwin, switch paths to Windows format before running javac
if $cygwin; then
javaSource=$(cygpath --path --windows "$javaSource")
javaClass=$(cygpath --path --windows "$javaClass")
fi
if [ -e "$javaSource" ]; then
if [ ! -e "$javaClass" ]; then
log " - Compiling MavenWrapperDownloader.java ..."
("$JAVA_HOME/bin/javac" "$javaSource")
fi
if [ -e "$javaClass" ]; then
log " - Running MavenWrapperDownloader.java ..."
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath"
fi
fi
fi
fi
##########################################################################################
# End of extension
##########################################################################################
# If specified, validate the SHA-256 sum of the Maven wrapper jar file
wrapperSha256Sum=""
while IFS="=" read -r key value; do
case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;;
esac
done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
if [ -n "$wrapperSha256Sum" ]; then
wrapperSha256Result=false
if command -v sha256sum > /dev/null; then
if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then
wrapperSha256Result=true
fi
elif command -v shasum > /dev/null; then
if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then
wrapperSha256Result=true
fi
else
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available."
echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties."
exit 1
fi
if [ $wrapperSha256Result = false ]; then
echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2
echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2
echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2
exit 1
fi
fi
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME")
[ -n "$CLASSPATH" ] &&
CLASSPATH=$(cygpath --path --windows "$CLASSPATH")
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR")
fi
# Provide a "standardized" way to retrieve the CLI args that will
# work with both Windows and non-Windows executions.
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*"
export MAVEN_CMD_LINE_ARGS
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
# shellcheck disable=SC2086 # safe args
exec "$JAVACMD" \
$MAVEN_OPTS \
$MAVEN_DEBUG_OPTS \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"

205
mvnw.cmd vendored Normal file
View File

@ -0,0 +1,205 @@
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM http://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Apache Maven Wrapper startup batch script, version 3.2.0
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
@REM e.g. to debug Maven itself, use
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
@REM ----------------------------------------------------------------------------
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
@echo off
@REM set title of command window
title %0
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
@REM set %HOME% to equivalent of $HOME
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
@REM Execute a user defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
:skipRcPre
@setlocal
set ERROR_CODE=0
@REM To isolate internal variables from possible post scripts, we use another setlocal
@setlocal
@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome
echo.
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init
echo.
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
@REM ==== END VALIDATION ====
:init
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
@REM Fallback to current working directory if not found.
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
set EXEC_DIR=%CD%
set WDIR=%EXEC_DIR%
:findBaseDir
IF EXIST "%WDIR%"\.mvn goto baseDirFound
cd ..
IF "%WDIR%"=="%CD%" goto baseDirNotFound
set WDIR=%CD%
goto findBaseDir
:baseDirFound
set MAVEN_PROJECTBASEDIR=%WDIR%
cd "%EXEC_DIR%"
goto endDetectBaseDir
:baseDirNotFound
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
cd "%EXEC_DIR%"
:endDetectBaseDir
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
@setlocal EnableExtensions EnableDelayedExpansion
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
:endReadAdditionalConfig
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B
)
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
if exist %WRAPPER_JAR% (
if "%MVNW_VERBOSE%" == "true" (
echo Found %WRAPPER_JAR%
)
) else (
if not "%MVNW_REPOURL%" == "" (
SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
)
if "%MVNW_VERBOSE%" == "true" (
echo Couldn't find %WRAPPER_JAR%, downloading it ...
echo Downloading from: %WRAPPER_URL%
)
powershell -Command "&{"^
"$webclient = new-object System.Net.WebClient;"^
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
"}"^
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^
"}"
if "%MVNW_VERBOSE%" == "true" (
echo Finished downloading %WRAPPER_JAR%
)
)
@REM End of extension
@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file
SET WRAPPER_SHA_256_SUM=""
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B
)
IF NOT %WRAPPER_SHA_256_SUM%=="" (
powershell -Command "&{"^
"$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^
"If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^
" Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^
" Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^
" Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^
" exit 1;"^
"}"^
"}"
if ERRORLEVEL 1 goto error
)
@REM Provide a "standardized" way to retrieve the CLI args that will
@REM work with both Windows and non-Windows executions.
set MAVEN_CMD_LINE_ARGS=%*
%MAVEN_JAVA_EXE% ^
%JVM_CONFIG_MAVEN_PROPS% ^
%MAVEN_OPTS% ^
%MAVEN_DEBUG_OPTS% ^
-classpath %WRAPPER_JAR% ^
"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
%WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
if ERRORLEVEL 1 goto error
goto end
:error
set ERROR_CODE=1
:end
@endlocal & set ERROR_CODE=%ERROR_CODE%
if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
@REM check for post script, once with legacy .bat ending and once with .cmd ending
if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
:skipRcPost
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
if "%MAVEN_BATCH_PAUSE%"=="on" pause
if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
cmd /C exit /B %ERROR_CODE%

21
ngsw-config.json Normal file
View File

@ -0,0 +1,21 @@
{
"$schema": "./node_modules/@angular/service-worker/config/schema.json",
"index": "/index.html",
"assetGroups": [
{
"name": "app",
"installMode": "prefetch",
"resources": {
"files": ["/favicon.ico", "/index.html", "/manifest.webapp", "/*.css", "/*.js"]
}
},
{
"name": "assets",
"installMode": "lazy",
"updateMode": "prefetch",
"resources": {
"files": ["/content/**", "/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)"]
}
}
]
}

42
npmw Normal file
View File

@ -0,0 +1,42 @@
#!/bin/sh
basedir=`dirname "$0"`
if [ -f "$basedir/mvnw" ]; then
bindir="$basedir/target/node"
repodir="$basedir/target/node/node_modules"
installCommand="$basedir/mvnw -Pwebapp frontend:install-node-and-npm@install-node-and-npm"
PATH="$basedir/$builddir/:$PATH"
NPM_EXE="$basedir/$builddir/node_modules/npm/bin/npm-cli.js"
NODE_EXE="$basedir/$builddir/node"
elif [ -f "$basedir/gradlew" ]; then
bindir="$basedir/build/node/bin"
repodir="$basedir/build/node/lib/node_modules"
installCommand="$basedir/gradlew npmSetup"
else
echo "Using npm installed globally"
exec npm "$@"
fi
NPM_EXE="$repodir/npm/bin/npm-cli.js"
NODE_EXE="$bindir/node"
if [ ! -x "$NPM_EXE" ] || [ ! -x "$NODE_EXE" ]; then
$installCommand || true
fi
if [ -x "$NODE_EXE" ]; then
echo "Using node installed locally $($NODE_EXE --version)"
PATH="$bindir:$PATH"
else
NODE_EXE='node'
fi
if [ ! -x "$NPM_EXE" ]; then
echo "Local npm not found, using npm installed globally"
npm "$@"
else
echo "Using npm installed locally $($NODE_EXE $NPM_EXE --version)"
$NODE_EXE $NPM_EXE "$@"
fi

31
npmw.cmd Normal file
View File

@ -0,0 +1,31 @@
@echo off
setlocal
set NPMW_DIR=%~dp0
if exist "%NPMW_DIR%mvnw.cmd" (
set NODE_EXE=^"^"
set NODE_PATH=%NPMW_DIR%target\node\
set NPM_EXE=^"%NPMW_DIR%target\node\npm.cmd^"
set INSTALL_NPM_COMMAND=^"%NPMW_DIR%mvnw.cmd^" -Pwebapp frontend:install-node-and-npm@install-node-and-npm
) else (
set NODE_EXE=^"%NPMW_DIR%build\node\bin\node.exe^"
set NODE_PATH=%NPMW_DIR%build\node\bin\
set NPM_EXE=^"%NPMW_DIR%build\node\lib\node_modules\npm\bin\npm-cli.js^"
set INSTALL_NPM_COMMAND=^"%NPMW_DIR%gradlew.bat^" npmSetup
)
if not exist %NPM_EXE% (
call %INSTALL_NPM_COMMAND%
)
if exist %NODE_EXE% (
Rem execute local npm with local node, whilst adding local node location to the PATH for this CMD session
endlocal & echo "%PATH%"|find /i "%NODE_PATH%;">nul || set "PATH=%NODE_PATH%;%PATH%" & call %NODE_EXE% %NPM_EXE% %*
) else if exist %NPM_EXE% (
Rem execute local npm, whilst adding local npm location to the PATH for this CMD session
endlocal & echo "%PATH%"|find /i "%NODE_PATH%;">nul || set "PATH=%NODE_PATH%;%PATH%" & call %NPM_EXE% %*
) else (
call npm %*
)

138
package.json Normal file
View File

@ -0,0 +1,138 @@
{
"name": "isdashboard",
"version": "0.0.1-SNAPSHOT",
"private": true,
"description": "Description for Isdashboard",
"license": "UNLICENSED",
"scripts": {
"app:start": "./mvnw",
"app:up": "docker compose -f src/main/docker/app.yml up --wait",
"backend:build-cache": "./mvnw dependency:go-offline",
"backend:debug": "./mvnw -Dspring-boot.run.jvmArguments=\"-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:8000\"",
"backend:doc:test": "./mvnw -ntp javadoc:javadoc --batch-mode",
"backend:info": "./mvnw -ntp enforcer:display-info --batch-mode",
"backend:nohttp:test": "./mvnw -ntp checkstyle:check --batch-mode",
"backend:start": "./mvnw -Dskip.installnodenpm -Dskip.npm",
"backend:unit:test": "./mvnw -ntp -Dskip.installnodenpm -Dskip.npm verify --batch-mode -Dlogging.level.ROOT=OFF -Dlogging.level.tech.jhipster=OFF -Dlogging.level.org.gcube.isdashboard=OFF -Dlogging.level.org.springframework=OFF -Dlogging.level.org.springframework.web=OFF -Dlogging.level.org.springframework.security=OFF",
"build": "npm run webapp:prod --",
"build-watch": "concurrently 'npm run webapp:build:dev -- --watch' npm:backend:start",
"ci:backend:test": "npm run backend:info && npm run backend:doc:test && npm run backend:nohttp:test && npm run backend:unit:test -- -P$npm_package_config_default_environment",
"ci:e2e:package": "npm run java:$npm_package_config_packaging:$npm_package_config_default_environment -- -Pe2e -Denforcer.skip=true",
"ci:e2e:prepare": "npm run ci:e2e:prepare:docker",
"ci:e2e:prepare:docker": "npm run services:up --if-present && docker ps -a",
"preci:e2e:server:start": "npm run services:db:await --if-present && npm run services:others:await --if-present",
"ci:e2e:server:start": "java -jar target/e2e.$npm_package_config_packaging --spring.profiles.active=e2e,$npm_package_config_default_environment -Dlogging.level.ROOT=OFF -Dlogging.level.tech.jhipster=OFF -Dlogging.level.org.gcube.isdashboard=OFF -Dlogging.level.org.springframework=OFF -Dlogging.level.org.springframework.web=OFF -Dlogging.level.org.springframework.security=OFF --logging.level.org.springframework.web=ERROR",
"ci:e2e:teardown": "npm run ci:e2e:teardown:docker --if-present",
"ci:frontend:build": "npm run webapp:build:$npm_package_config_default_environment",
"ci:frontend:test": "npm run ci:frontend:build && npm test",
"clean-www": "rimraf target/classes/static/app/{src,target/}",
"cleanup": "rimraf target/classes/static/",
"docker:db:up": "echo \"Docker for db no not configured for application isdashboard\"",
"java:docker": "./mvnw -ntp verify -DskipTests -Pprod jib:dockerBuild",
"java:docker:arm64": "npm run java:docker -- -Djib-maven-plugin.architecture=arm64",
"java:docker:dev": "npm run java:docker -- -Pdev,webapp",
"java:docker:prod": "npm run java:docker -- -Pprod",
"java:jar": "./mvnw -ntp verify -DskipTests --batch-mode",
"java:jar:dev": "npm run java:jar -- -Pdev,webapp",
"java:jar:prod": "npm run java:jar -- -Pprod",
"java:war": "./mvnw -ntp verify -DskipTests --batch-mode -Pwar",
"java:war:dev": "npm run java:war -- -Pdev,webapp",
"java:war:prod": "npm run java:war -- -Pprod",
"jest": "jest --coverage --logHeapUsage --maxWorkers=2 --config jest.conf.js",
"lint": "eslint . --ext .js,.ts",
"lint:fix": "npm run lint -- --fix",
"prepare": "husky install",
"prettier:check": "prettier --check \"{,src/**/,webpack/,.blueprint/**/}*.{md,json,yml,html,cjs,mjs,js,ts,tsx,css,scss,java}\"",
"prettier:format": "prettier --write \"{,src/**/,webpack/,.blueprint/**/}*.{md,json,yml,html,cjs,mjs,js,ts,tsx,css,scss,java}\"",
"serve": "npm run start --",
"start": "ng serve --hmr",
"start-tls": "npm run webapp:dev-ssl",
"pretest": "npm run lint",
"test": "ng test --coverage --log-heap-usage -w=2",
"test:watch": "npm run test -- --watch",
"watch": "concurrently npm:start npm:backend:start",
"webapp:build": "npm run clean-www && npm run webapp:build:dev",
"webapp:build:dev": "ng build --configuration development",
"webapp:build:prod": "ng build --configuration production",
"webapp:dev": "ng serve",
"webapp:dev-ssl": "ng serve --ssl",
"webapp:dev-verbose": "ng serve --verbose",
"webapp:prod": "npm run clean-www && npm run webapp:build:prod",
"webapp:test": "npm run test --"
},
"config": {
"backend_port": 8080,
"default_environment": "prod",
"packaging": "jar"
},
"dependencies": {
"@angular/common": "16.1.4",
"@angular/compiler": "16.1.4",
"@angular/core": "16.1.4",
"@angular/forms": "16.1.4",
"@angular/localize": "16.1.4",
"@angular/platform-browser": "16.1.4",
"@angular/platform-browser-dynamic": "16.1.4",
"@angular/router": "16.1.4",
"@fortawesome/angular-fontawesome": "0.13.0",
"@fortawesome/fontawesome-svg-core": "6.4.0",
"@fortawesome/free-solid-svg-icons": "6.4.0",
"@ng-bootstrap/ng-bootstrap": "15.1.0",
"@popperjs/core": "2.11.8",
"bootstrap": "5.3.0",
"dayjs": "1.11.9",
"ngx-infinite-scroll": "16.0.0",
"rxjs": "7.8.1",
"tslib": "2.6.0",
"zone.js": "0.13.1"
},
"devDependencies": {
"@angular-builders/custom-webpack": "16.0.0",
"@angular-builders/jest": "16.0.0",
"@angular-devkit/build-angular": "16.1.4",
"@angular-eslint/eslint-plugin": "16.0.2",
"@angular/cli": "16.1.4",
"@angular/compiler-cli": "16.1.4",
"@angular/service-worker": "16.1.4",
"@types/jest": "29.5.3",
"@types/node": "18.16.19",
"@typescript-eslint/eslint-plugin": "5.61.0",
"@typescript-eslint/parser": "5.61.0",
"browser-sync": "2.29.3",
"browser-sync-webpack-plugin": "2.3.0",
"concurrently": "8.2.0",
"copy-webpack-plugin": "11.0.0",
"eslint": "8.44.0",
"eslint-config-prettier": "8.8.0",
"eslint-webpack-plugin": "4.0.1",
"generator-jhipster": "8.0.0-beta.2",
"husky": "8.0.3",
"jest": "29.6.1",
"jest-date-mock": "1.0.8",
"jest-environment-jsdom": "29.6.1",
"jest-junit": "16.0.0",
"jest-preset-angular": "13.1.1",
"jest-sonar": "0.2.16",
"lint-staged": "13.2.3",
"prettier": "2.8.8",
"prettier-plugin-java": "2.2.0",
"prettier-plugin-packagejson": "2.4.5",
"rimraf": "5.0.1",
"swagger-ui-dist": "5.1.0",
"ts-jest": "29.1.1",
"typescript": "5.1.6",
"wait-on": "7.0.1",
"webpack-bundle-analyzer": "4.9.0",
"webpack-merge": "5.9.0",
"webpack-notifier": "1.15.0"
},
"engines": {
"node": ">=18.16.1"
},
"cacheDirectories": [
"node_modules"
],
"overrides": {
"webpack": "5.88.1"
}
}

1033
pom.xml Normal file

File diff suppressed because it is too large Load Diff

31
sonar-project.properties Normal file
View File

@ -0,0 +1,31 @@
sonar.projectKey=isdashboard
sonar.projectName=isdashboard generated by jhipster
# Typescript tests files must be inside sources and tests, othewise `INFO: Test execution data ignored for 80 unknown files, including:` is shown.
sonar.sources=src
sonar.tests=src
sonar.host.url=http://localhost:9001
sonar.test.inclusions=src/test/**/*.*, src/main/webapp/app/**/*.spec.ts
sonar.coverage.jacoco.xmlReportPaths=target/site/**/jacoco*.xml
sonar.java.codeCoveragePlugin=jacoco
sonar.junit.reportPaths=target/surefire-reports,target/failsafe-reports
sonar.testExecutionReportPaths=target/test-results/jest/TESTS-results-sonar.xml
sonar.javascript.lcov.reportPaths=target/test-results/lcov.info
sonar.sourceEncoding=UTF-8
sonar.exclusions=src/main/webapp/content/**/*.*, src/main/webapp/i18n/*.js, target/classes/static/**/*.*
sonar.issue.ignore.multicriteria=S3437,S4684,S5145,UndocumentedApi
# Rule https://rules.sonarsource.com/java/RSPEC-3437 is ignored, as a JPA-managed field cannot be transient
sonar.issue.ignore.multicriteria.S3437.resourceKey=src/main/java/**/*
sonar.issue.ignore.multicriteria.S3437.ruleKey=squid:S3437
# Rule https://rules.sonarsource.com/java/RSPEC-4684
sonar.issue.ignore.multicriteria.S4684.resourceKey=src/main/java/**/*
sonar.issue.ignore.multicriteria.S4684.ruleKey=java:S4684
# Rule https://rules.sonarsource.com/java/RSPEC-5145 log filter is applied
sonar.issue.ignore.multicriteria.S5145.resourceKey=src/main/java/**/*
sonar.issue.ignore.multicriteria.S5145.ruleKey=javasecurity:S5145
# Rule https://rules.sonarsource.com/java/RSPEC-1176 is ignored, as we want to follow "clean code" guidelines and classes, methods and arguments names should be self-explanatory
sonar.issue.ignore.multicriteria.UndocumentedApi.resourceKey=src/main/java/**/*
sonar.issue.ignore.multicriteria.UndocumentedApi.ruleKey=squid:UndocumentedApi

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

@ -0,0 +1,18 @@
# This configuration is intended for development purpose, it's **your** responsibility to harden it for production
name: isdashboard
services:
app:
image: isdashboard
environment:
- _JAVA_OPTIONS=-Xmx512m -Xms256m
- SPRING_PROFILES_ACTIVE=prod,api-docs
- MANAGEMENT_PROMETHEUS_METRICS_EXPORT_ENABLED=true
# If you want to expose these ports outside your dev PC,
# remove the "127.0.0.1:" prefix
ports:
- 127.0.0.1:8080:8080
healthcheck:
test: ['CMD', 'curl', '-f', 'http://localhost:8080/management/health']
interval: 5s
timeout: 5s
retries: 40

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,11 @@
apiVersion: 1
providers:
- name: 'Prometheus'
orgId: 1
folder: ''
type: file
disableDeletion: false
editable: true
options:
path: /etc/grafana/provisioning/dashboards

View File

@ -0,0 +1,50 @@
apiVersion: 1
# list of datasources that should be deleted from the database
deleteDatasources:
- name: Prometheus
orgId: 1
# list of datasources to insert/update depending
# whats available in the database
datasources:
# <string, required> name of the datasource. Required
- name: Prometheus
# <string, required> datasource type. Required
type: prometheus
# <string, required> access mode. direct or proxy. Required
access: proxy
# <int> org id. will default to orgId 1 if not specified
orgId: 1
# <string> url
# On MacOS, replace localhost by host.docker.internal
url: http://localhost:9090
# <string> database password, if used
password:
# <string> database user, if used
user:
# <string> database name, if used
database:
# <bool> enable/disable basic auth
basicAuth: false
# <string> basic auth username
basicAuthUser: admin
# <string> basic auth password
basicAuthPassword: admin
# <bool> enable/disable with credentials headers
withCredentials:
# <bool> mark as default datasource. Max one per org
isDefault: true
# <map> fields that will be converted to json and stored in json_data
jsonData:
graphiteVersion: '1.1'
tlsAuth: false
tlsAuthWithCACert: false
# <string> json object of data that will be encrypted.
secureJsonData:
tlsCACert: '...'
tlsClientCert: '...'
tlsClientKey: '...'
version: 1
# <bool> allow users to edit datasources from the UI.
editable: true

View File

@ -0,0 +1,48 @@
## How to use JHCC docker compose
# To allow JHCC to reach JHipster application from a docker container note that we set the host as host.docker.internal
# To reach the application from a browser, you need to add '127.0.0.1 host.docker.internal' to your hosts file.
### Discovery mode
# JHCC support 3 kinds of discovery mode: Consul, Eureka and static
# In order to use one, please set SPRING_PROFILES_ACTIVE to one (and only one) of this values: consul,eureka,static
### Discovery properties
# According to the discovery mode choose as Spring profile, you have to set the right properties
# please note that current properties are set to run JHCC with default values, personalize them if needed
# and remove those from other modes. You can only have one mode active.
#### Eureka
# - EUREKA_CLIENT_SERVICE_URL_DEFAULTZONE=http://admin:admin@host.docker.internal:8761/eureka/
#### Consul
# - SPRING_CLOUD_CONSUL_HOST=host.docker.internal
# - SPRING_CLOUD_CONSUL_PORT=8500
#### Static
# Add instances to "MyApp"
# - SPRING_CLOUD_DISCOVERY_CLIENT_SIMPLE_INSTANCES_MYAPP_0_URI=http://host.docker.internal:8081
# - SPRING_CLOUD_DISCOVERY_CLIENT_SIMPLE_INSTANCES_MYAPP_1_URI=http://host.docker.internal:8082
# Or add a new application named MyNewApp
# - SPRING_CLOUD_DISCOVERY_CLIENT_SIMPLE_INSTANCES_MYNEWAPP_0_URI=http://host.docker.internal:8080
# This configuration is intended for development purpose, it's **your** responsibility to harden it for production
#### IMPORTANT
# If you choose Consul or Eureka mode:
# Do not forget to remove the prefix "127.0.0.1" in front of their port in order to expose them.
# This is required because JHCC need to communicate with Consul or Eureka.
# - In Consul mode, the ports are in the consul.yml file.
# - In Eureka mode, the ports are in the jhipster-registry.yml file.
name: isdashboard
services:
jhipster-control-center:
image: 'jhipster/jhipster-control-center:v0.5.0'
command:
- /bin/sh
- -c
# Patch /etc/hosts to support resolving host.docker.internal to the internal IP address used by the host in all OSes
- echo "`ip route | grep default | cut -d ' ' -f3` host.docker.internal" | tee -a /etc/hosts > /dev/null && java -jar /jhipster-control-center.jar
environment:
- _JAVA_OPTIONS=-Xmx512m -Xms256m
- SPRING_PROFILES_ACTIVE=prod,api-docs,static
- SPRING_CLOUD_DISCOVERY_CLIENT_SIMPLE_INSTANCES_ISDASHBOARD_0_URI=http://host.docker.internal:8080
- LOGGING_FILE_NAME=/tmp/jhipster-control-center.log
# If you want to expose these ports outside your dev PC,
# remove the "127.0.0.1:" prefix
ports:
- 127.0.0.1:7419:7419

View File

@ -0,0 +1,39 @@
#!/bin/bash
echo "The application will start in ${JHIPSTER_SLEEP}s..." && sleep ${JHIPSTER_SLEEP}
# usage: file_env VAR [DEFAULT]
# ie: file_env 'XYZ_DB_PASSWORD' 'example'
# (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of
# "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature)
file_env() {
local var="$1"
local fileVar="${var}_FILE"
local def="${2:-}"
if [[ ${!var:-} && ${!fileVar:-} ]]; then
echo >&2 "error: both $var and $fileVar are set (but are exclusive)"
exit 1
fi
local val="$def"
if [[ ${!var:-} ]]; then
val="${!var}"
elif [[ ${!fileVar:-} ]]; then
val="$(< "${!fileVar}")"
fi
if [[ -n $val ]]; then
export "$var"="$val"
fi
unset "$fileVar"
}
file_env 'SPRING_DATASOURCE_URL'
file_env 'SPRING_DATASOURCE_USERNAME'
file_env 'SPRING_DATASOURCE_PASSWORD'
file_env 'SPRING_LIQUIBASE_URL'
file_env 'SPRING_LIQUIBASE_USER'
file_env 'SPRING_LIQUIBASE_PASSWORD'
file_env 'JHIPSTER_REGISTRY_PASSWORD'
exec java ${JAVA_OPTS} -noverify -XX:+AlwaysPreTouch -Djava.security.egd=file:/dev/./urandom -cp /app/resources/:/app/classes/:/app/libs/* "org.gcube.isdashboard.IsdashboardApp" "$@"

View File

@ -0,0 +1,31 @@
# This configuration is intended for development purpose, it's **your** responsibility to harden it for production
name: isdashboard
services:
prometheus:
image: prom/prometheus:v2.45.0
volumes:
- ./prometheus/:/etc/prometheus/
command:
- '--config.file=/etc/prometheus/prometheus.yml'
# If you want to expose these ports outside your dev PC,
# remove the "127.0.0.1:" prefix
ports:
- 127.0.0.1:9090:9090
# On MacOS, remove next line and replace localhost by host.docker.internal in prometheus/prometheus.yml and
# grafana/provisioning/datasources/datasource.yml
network_mode: 'host' # to test locally running service
grafana:
image: grafana/grafana:10.0.2
volumes:
- ./grafana/provisioning/:/etc/grafana/provisioning/
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
- GF_USERS_ALLOW_SIGN_UP=false
- GF_INSTALL_PLUGINS=grafana-piechart-panel
# If you want to expose these ports outside your dev PC,
# remove the "127.0.0.1:" prefix
ports:
- 127.0.0.1:3000:3000
# On MacOS, remove next line and replace localhost by host.docker.internal in prometheus/prometheus.yml and
# grafana/provisioning/datasources/datasource.yml
network_mode: 'host' # to test locally running service

View File

@ -0,0 +1,31 @@
# Sample global config for monitoring JHipster applications
global:
scrape_interval: 15s # By default, scrape targets every 15 seconds.
evaluation_interval: 15s # By default, scrape targets every 15 seconds.
# scrape_timeout is set to the global default (10s).
# Attach these labels to any time series or alerts when communicating with
# external systems (federation, remote storage, Alertmanager).
external_labels:
monitor: 'jhipster'
# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
- job_name: 'prometheus'
# Override the global default and scrape targets from this job every 5 seconds.
scrape_interval: 5s
# scheme defaults to 'http' enable https in case your application is server via https
#scheme: https
# basic auth is not needed by default. See https://www.jhipster.tech/monitoring/#configuring-metrics-forwarding for details
#basic_auth:
# username: admin
# password: admin
metrics_path: /management/prometheus
static_configs:
- targets:
# On MacOS, replace localhost by host.docker.internal
- localhost:8080

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

@ -0,0 +1,15 @@
# This configuration is intended for development purpose, it's **your** responsibility to harden it for production
name: isdashboard
services:
sonar:
container_name: sonarqube
image: sonarqube:10.1.0-community
# Forced authentication redirect for UI is turned off for out of the box experience while trying out SonarQube
# For real use cases delete SONAR_FORCEAUTHENTICATION variable or set SONAR_FORCEAUTHENTICATION=true
environment:
- SONAR_FORCEAUTHENTICATION=false
# If you want to expose these ports outside your dev PC,
# remove the "127.0.0.1:" prefix
ports:
- 127.0.0.1:9001:9000
- 127.0.0.1:9000:9000

View File

@ -0,0 +1,19 @@
package org.gcube.isdashboard;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import tech.jhipster.config.DefaultProfileUtil;
/**
* This is a helper Java class that provides an alternative to creating a {@code web.xml}.
* This will be invoked only when the application is deployed to a Servlet container like Tomcat, JBoss etc.
*/
public class ApplicationWebXml extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
// set a default to use when no profile is configured.
DefaultProfileUtil.addDefaultProfile(application.application());
return application.sources(IsdashboardApp.class);
}
}

View File

@ -0,0 +1,13 @@
package org.gcube.isdashboard;
import jakarta.annotation.Generated;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Generated(value = "JHipster", comments = "Generated by JHipster 8.0.0-beta.2")
@Retention(RetentionPolicy.SOURCE)
@Target({ ElementType.TYPE })
public @interface GeneratedByJHipster {
}

View File

@ -0,0 +1,107 @@
package org.gcube.isdashboard;
import jakarta.annotation.PostConstruct;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Optional;
import org.apache.commons.lang3.StringUtils;
import org.gcube.isdashboard.config.ApplicationProperties;
import org.gcube.isdashboard.config.CRLFLogConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.core.env.Environment;
import tech.jhipster.config.DefaultProfileUtil;
import tech.jhipster.config.JHipsterConstants;
@SpringBootApplication
@EnableConfigurationProperties({ ApplicationProperties.class })
public class IsdashboardApp {
private static final Logger log = LoggerFactory.getLogger(IsdashboardApp.class);
private final Environment env;
public IsdashboardApp(Environment env) {
this.env = env;
}
/**
* Initializes isdashboard.
* <p>
* Spring profiles can be configured with a program argument --spring.profiles.active=your-active-profile
* <p>
* You can find more information on how profiles work with JHipster on <a href="https://www.jhipster.tech/profiles/">https://www.jhipster.tech/profiles/</a>.
*/
@PostConstruct
public void initApplication() {
Collection<String> activeProfiles = Arrays.asList(env.getActiveProfiles());
if (
activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) &&
activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_PRODUCTION)
) {
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(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) &&
activeProfiles.contains(JHipsterConstants.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."
);
}
}
/**
* Main method, used to run the application.
*
* @param args the command line arguments.
*/
public static void main(String[] args) {
SpringApplication app = new SpringApplication(IsdashboardApp.class);
DefaultProfileUtil.addDefaultProfile(app);
Environment env = app.run(args).getEnvironment();
logApplicationStartup(env);
}
private static void logApplicationStartup(Environment env) {
String protocol = Optional.ofNullable(env.getProperty("server.ssl.key-store")).map(key -> "https").orElse("http");
String serverPort = env.getProperty("server.port");
String contextPath = Optional
.ofNullable(env.getProperty("server.servlet.context-path"))
.filter(StringUtils::isNotBlank)
.orElse("/");
String hostAddress = "localhost";
try {
hostAddress = InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
log.warn("The host name could not be determined, using `localhost` as fallback");
}
log.info(
CRLFLogConverter.CRLF_SAFE_MARKER,
"""
----------------------------------------------------------
\tApplication '{}' is running! Access URLs:
\tLocal: \t\t{}://localhost:{}{}
\tExternal: \t{}://{}:{}{}
\tProfile(s): \t{}
----------------------------------------------------------""",
env.getProperty("spring.application.name"),
protocol,
serverPort,
contextPath,
protocol,
hostAddress,
serverPort,
contextPath,
env.getActiveProfiles().length == 0 ? env.getDefaultProfiles() : env.getActiveProfiles()
);
}
}

View File

@ -0,0 +1,115 @@
package org.gcube.isdashboard.aop.logging;
import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.Environment;
import org.springframework.core.env.Profiles;
import tech.jhipster.config.JHipsterConstants;
/**
* Aspect for logging execution of service and repository Spring components.
*
* By default, it only runs with the "dev" profile.
*/
@Aspect
public class LoggingAspect {
private final Environment env;
public LoggingAspect(Environment env) {
this.env = env;
}
/**
* Pointcut that matches all repositories, services and Web REST endpoints.
*/
@Pointcut(
"within(@org.springframework.stereotype.Repository *)" +
" || within(@org.springframework.stereotype.Service *)" +
" || within(@org.springframework.web.bind.annotation.RestController *)"
)
public void springBeanPointcut() {
// Method is empty as this is just a Pointcut, the implementations are in the advices.
}
/**
* Pointcut that matches all Spring beans in the application's main packages.
*/
@Pointcut(
"within(org.gcube.isdashboard.repository..*)" +
" || within(org.gcube.isdashboard.service..*)" +
" || within(org.gcube.isdashboard.web.rest..*)"
)
public void applicationPackagePointcut() {
// Method is empty as this is just a Pointcut, the implementations are in the advices.
}
/**
* Retrieves the {@link Logger} associated to the given {@link JoinPoint}.
*
* @param joinPoint join point we want the logger for.
* @return {@link Logger} associated to the given {@link JoinPoint}.
*/
private Logger logger(JoinPoint joinPoint) {
return LoggerFactory.getLogger(joinPoint.getSignature().getDeclaringTypeName());
}
/**
* Advice that logs methods throwing exceptions.
*
* @param joinPoint join point for advice.
* @param e exception.
*/
@AfterThrowing(pointcut = "applicationPackagePointcut() && springBeanPointcut()", throwing = "e")
public void logAfterThrowing(JoinPoint joinPoint, Throwable e) {
if (env.acceptsProfiles(Profiles.of(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT))) {
logger(joinPoint)
.error(
"Exception in {}() with cause = '{}' and exception = '{}'",
joinPoint.getSignature().getName(),
e.getCause() != null ? e.getCause() : "NULL",
e.getMessage(),
e
);
} else {
logger(joinPoint)
.error(
"Exception in {}() with cause = {}",
joinPoint.getSignature().getName(),
e.getCause() != null ? e.getCause() : "NULL"
);
}
}
/**
* Advice that logs when a method is entered and exited.
*
* @param joinPoint join point for advice.
* @return result.
* @throws Throwable throws {@link IllegalArgumentException}.
*/
@Around("applicationPackagePointcut() && springBeanPointcut()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
Logger log = logger(joinPoint);
if (log.isDebugEnabled()) {
log.debug("Enter: {}() with argument[s] = {}", joinPoint.getSignature().getName(), Arrays.toString(joinPoint.getArgs()));
}
try {
Object result = joinPoint.proceed();
if (log.isDebugEnabled()) {
log.debug("Exit: {}() with result = {}", joinPoint.getSignature().getName(), result);
}
return result;
} catch (IllegalArgumentException e) {
log.error("Illegal argument: {} in {}()", Arrays.toString(joinPoint.getArgs()), joinPoint.getSignature().getName());
throw e;
}
}
}

View File

@ -0,0 +1,4 @@
/**
* Logging aspect.
*/
package org.gcube.isdashboard.aop.logging;

View File

@ -0,0 +1,16 @@
package org.gcube.isdashboard.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Properties specific to Isdashboard.
* <p>
* Properties are configured in the {@code application.yml} file.
* See {@link tech.jhipster.config.JHipsterProperties} for a good example.
*/
@ConfigurationProperties(prefix = "application", ignoreUnknownFields = false)
public class ApplicationProperties {
// jhipster-needle-application-properties-property
// jhipster-needle-application-properties-property-getter
// jhipster-needle-application-properties-property-class
}

View File

@ -0,0 +1,48 @@
package org.gcube.isdashboard.config;
import java.util.concurrent.Executor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler;
import org.springframework.boot.autoconfigure.task.TaskExecutionProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import tech.jhipster.async.ExceptionHandlingAsyncTaskExecutor;
@Configuration
@EnableAsync
@EnableScheduling
@Profile("!testdev & !testprod")
public class AsyncConfiguration implements AsyncConfigurer {
private final Logger log = LoggerFactory.getLogger(AsyncConfiguration.class);
private final TaskExecutionProperties taskExecutionProperties;
public AsyncConfiguration(TaskExecutionProperties taskExecutionProperties) {
this.taskExecutionProperties = taskExecutionProperties;
}
@Override
@Bean(name = "taskExecutor")
public Executor getAsyncExecutor() {
log.debug("Creating Async Task Executor");
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(taskExecutionProperties.getPool().getCoreSize());
executor.setMaxPoolSize(taskExecutionProperties.getPool().getMaxSize());
executor.setQueueCapacity(taskExecutionProperties.getPool().getQueueCapacity());
executor.setThreadNamePrefix(taskExecutionProperties.getThreadNamePrefix());
return new ExceptionHandlingAsyncTaskExecutor(executor);
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new SimpleAsyncUncaughtExceptionHandler();
}
}

View File

@ -0,0 +1,67 @@
package org.gcube.isdashboard.config;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.pattern.CompositeConverter;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;
import org.springframework.boot.ansi.AnsiColor;
import org.springframework.boot.ansi.AnsiElement;
import org.springframework.boot.ansi.AnsiOutput;
import org.springframework.boot.ansi.AnsiStyle;
/**
* Log filter to prevent attackers from forging log entries by submitting input containing CRLF characters.
* CRLF characters are replaced with a red colored _ character.
*
* @see <a href="https://owasp.org/www-community/attacks/Log_Injection">Log Forging Description</a>
* @see <a href="https://github.com/jhipster/generator-jhipster/issues/14949">JHipster issue</a>
*/
public class CRLFLogConverter extends CompositeConverter<ILoggingEvent> {
public static final Marker CRLF_SAFE_MARKER = MarkerFactory.getMarker("CRLF_SAFE");
private static final String[] SAFE_LOGGERS = {
"org.hibernate",
"org.springframework.boot.autoconfigure",
"org.springframework.boot.diagnostics",
};
private static final Map<String, AnsiElement> ELEMENTS;
static {
Map<String, AnsiElement> ansiElements = new HashMap<>();
ansiElements.put("faint", AnsiStyle.FAINT);
ansiElements.put("red", AnsiColor.RED);
ansiElements.put("green", AnsiColor.GREEN);
ansiElements.put("yellow", AnsiColor.YELLOW);
ansiElements.put("blue", AnsiColor.BLUE);
ansiElements.put("magenta", AnsiColor.MAGENTA);
ansiElements.put("cyan", AnsiColor.CYAN);
ELEMENTS = Collections.unmodifiableMap(ansiElements);
}
@Override
protected String transform(ILoggingEvent event, String in) {
AnsiElement element = ELEMENTS.get(getFirstOption());
if ((event.getMarker() != null && event.getMarker().contains(CRLF_SAFE_MARKER)) || isLoggerSafe(event)) {
return in;
}
String replacement = element == null ? "_" : toAnsiString("_", element);
return in.replaceAll("[\n\r\t]", replacement);
}
protected boolean isLoggerSafe(ILoggingEvent event) {
for (String safeLogger : SAFE_LOGGERS) {
if (event.getLoggerName().startsWith(safeLogger)) {
return true;
}
}
return false;
}
protected String toAnsiString(String in, AnsiElement element) {
return AnsiOutput.toString(element, in);
}
}

View File

@ -0,0 +1,66 @@
package org.gcube.isdashboard.config;
import java.time.Duration;
import org.ehcache.config.builders.*;
import org.ehcache.jsr107.Eh107Configuration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.cache.JCacheManagerCustomizer;
import org.springframework.boot.info.BuildProperties;
import org.springframework.boot.info.GitProperties;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.*;
import tech.jhipster.config.JHipsterProperties;
import tech.jhipster.config.cache.PrefixedKeyGenerator;
@Configuration
@EnableCaching
public class CacheConfiguration {
private GitProperties gitProperties;
private BuildProperties buildProperties;
private final javax.cache.configuration.Configuration<Object, Object> jcacheConfiguration;
public CacheConfiguration(JHipsterProperties jHipsterProperties) {
JHipsterProperties.Cache.Ehcache ehcache = jHipsterProperties.getCache().getEhcache();
jcacheConfiguration =
Eh107Configuration.fromEhcacheCacheConfiguration(
CacheConfigurationBuilder
.newCacheConfigurationBuilder(Object.class, Object.class, ResourcePoolsBuilder.heap(ehcache.getMaxEntries()))
.withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofSeconds(ehcache.getTimeToLiveSeconds())))
.build()
);
}
@Bean
public JCacheManagerCustomizer cacheManagerCustomizer() {
return cm -> {
// jhipster-needle-ehcache-add-entry
};
}
private void createCache(javax.cache.CacheManager cm, String cacheName) {
javax.cache.Cache<Object, Object> cache = cm.getCache(cacheName);
if (cache != null) {
cache.clear();
} else {
cm.createCache(cacheName, jcacheConfiguration);
}
}
@Autowired(required = false)
public void setGitProperties(GitProperties gitProperties) {
this.gitProperties = gitProperties;
}
@Autowired(required = false)
public void setBuildProperties(BuildProperties buildProperties) {
this.buildProperties = buildProperties;
}
@Bean
public KeyGenerator keyGenerator() {
return new PrefixedKeyGenerator(this.gitProperties, this.buildProperties);
}
}

View File

@ -0,0 +1,20 @@
package org.gcube.isdashboard.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.format.datetime.standard.DateTimeFormatterRegistrar;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* Configure the converters to use the ISO format for dates by default.
*/
@Configuration
public class DateTimeFormatConfiguration implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
registrar.setUseIsoFormat(true);
registrar.registerFormatters(registry);
}
}

View File

@ -0,0 +1,24 @@
package org.gcube.isdashboard.config;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class JacksonConfiguration {
/**
* Support for Java date and time API.
* @return the corresponding Jackson module.
*/
@Bean
public JavaTimeModule javaTimeModule() {
return new JavaTimeModule();
}
@Bean
public Jdk8Module jdk8TimeModule() {
return new Jdk8Module();
}
}

View File

@ -0,0 +1,26 @@
package org.gcube.isdashboard.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import tech.jhipster.config.locale.AngularCookieLocaleResolver;
@Configuration
public class LocaleConfiguration implements WebMvcConfigurer {
@Bean
public LocaleResolver localeResolver() {
AngularCookieLocaleResolver cookieLocaleResolver = new AngularCookieLocaleResolver();
cookieLocaleResolver.setCookieName("NG_TRANSLATE_LANG_KEY");
return cookieLocaleResolver;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
localeChangeInterceptor.setParamName("language");
registry.addInterceptor(localeChangeInterceptor);
}
}

View File

@ -0,0 +1,17 @@
package org.gcube.isdashboard.config;
import org.gcube.isdashboard.aop.logging.LoggingAspect;
import org.springframework.context.annotation.*;
import org.springframework.core.env.Environment;
import tech.jhipster.config.JHipsterConstants;
@Configuration
@EnableAspectJAutoProxy
public class LoggingAspectConfiguration {
@Bean
@Profile(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT)
public LoggingAspect loggingAspect(Environment env) {
return new LoggingAspect(env);
}
}

View File

@ -0,0 +1,47 @@
package org.gcube.isdashboard.config;
import static tech.jhipster.config.logging.LoggingUtils.*;
import ch.qos.logback.classic.LoggerContext;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import tech.jhipster.config.JHipsterProperties;
/*
* Configures the console and Logstash log appenders from the app properties
*/
@Configuration
public class LoggingConfiguration {
public LoggingConfiguration(
@Value("${spring.application.name}") String appName,
@Value("${server.port}") String serverPort,
JHipsterProperties jHipsterProperties,
ObjectMapper mapper
) throws JsonProcessingException {
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
Map<String, String> map = new HashMap<>();
map.put("app_name", appName);
map.put("app_port", serverPort);
String customFields = mapper.writeValueAsString(map);
JHipsterProperties.Logging loggingProperties = jHipsterProperties.getLogging();
JHipsterProperties.Logging.Logstash logstashProperties = loggingProperties.getLogstash();
if (loggingProperties.isUseJsonFormat()) {
addJsonConsoleAppender(context, customFields);
}
if (logstashProperties.isEnabled()) {
addLogstashTcpSocketAppender(context, customFields, logstashProperties);
}
if (loggingProperties.isUseJsonFormat() || logstashProperties.isEnabled()) {
addContextListener(context, customFields, loggingProperties);
}
}
}

View File

@ -0,0 +1,95 @@
package org.gcube.isdashboard.config;
import static org.springframework.security.config.Customizer.withDefaults;
import org.gcube.isdashboard.security.*;
import org.gcube.isdashboard.web.filter.SpaWebFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer.FrameOptionsConfig;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;
import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import tech.jhipster.config.JHipsterProperties;
import tech.jhipster.web.filter.CookieCsrfFilter;
@Configuration
@EnableMethodSecurity(securedEnabled = true)
public class SecurityConfiguration {
private final JHipsterProperties jHipsterProperties;
public SecurityConfiguration(JHipsterProperties jHipsterProperties) {
this.jHipsterProperties = jHipsterProperties;
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.cors(withDefaults())
.csrf(csrf ->
csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
// See https://stackoverflow.com/q/74447118/65681
.csrfTokenRequestHandler(new CsrfTokenRequestAttributeHandler())
)
.addFilterAfter(new SpaWebFilter(), BasicAuthenticationFilter.class)
.addFilterAfter(new CookieCsrfFilter(), BasicAuthenticationFilter.class)
.headers(headers ->
headers
.contentSecurityPolicy(csp -> csp.policyDirectives(jHipsterProperties.getSecurity().getContentSecurityPolicy()))
.frameOptions(FrameOptionsConfig::sameOrigin)
.referrerPolicy(referrer -> referrer.policy(ReferrerPolicyHeaderWriter.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN))
.permissionsPolicy(permissions ->
permissions.policy(
"camera=(), fullscreen=(self), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), sync-xhr=()"
)
)
)
.authorizeHttpRequests(authz ->
// prettier-ignore
authz
.requestMatchers("/", "/index.html", "/*.js", "/*.map", "/*.css").permitAll()
.requestMatchers("/*.ico", "/*.png", "/*.svg", "/*.webapp").permitAll()
.requestMatchers("/app/**").permitAll()
.requestMatchers("/i18n/**").permitAll()
.requestMatchers("/content/**").permitAll()
.requestMatchers("/swagger-ui/**").permitAll()
.requestMatchers("/api/authenticate").permitAll()
.requestMatchers("/api/admin/**").hasAuthority(AuthoritiesConstants.ADMIN)
.requestMatchers("/api/**").authenticated()
.requestMatchers("/v3/api-docs/**").hasAuthority(AuthoritiesConstants.ADMIN)
.requestMatchers("/management/health").permitAll()
.requestMatchers("/management/health/**").permitAll()
.requestMatchers("/management/info").permitAll()
.requestMatchers("/management/prometheus").permitAll()
.requestMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN)
)
.exceptionHandling(exceptionHanding ->
exceptionHanding.defaultAuthenticationEntryPointFor(
new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED),
new OrRequestMatcher(new AntPathRequestMatcher("/api/**"))
)
)
.formLogin(formLogin ->
formLogin
.loginProcessingUrl("/api/authentication")
.successHandler((request, response, authentication) -> response.setStatus(HttpStatus.OK.value()))
.failureHandler((request, response, exception) -> response.setStatus(HttpStatus.UNAUTHORIZED.value()))
.permitAll()
)
.logout(logout ->
logout.logoutUrl("/api/logout").logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler()).permitAll()
);
return http.build();
}
}

View File

@ -0,0 +1,59 @@
package org.gcube.isdashboard.config;
import java.util.concurrent.TimeUnit;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.http.CacheControl;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import tech.jhipster.config.JHipsterConstants;
import tech.jhipster.config.JHipsterProperties;
@Configuration
@Profile({ JHipsterConstants.SPRING_PROFILE_PRODUCTION })
public class StaticResourcesWebConfiguration implements WebMvcConfigurer {
protected static final String[] RESOURCE_LOCATIONS = new String[] {
"classpath:/static/",
"classpath:/static/content/",
"classpath:/static/i18n/",
};
protected static final String[] RESOURCE_PATHS = new String[] {
"/*.js",
"/*.css",
"/*.svg",
"/*.png",
"*.ico",
"/content/**",
"/i18n/*",
};
private final JHipsterProperties jhipsterProperties;
public StaticResourcesWebConfiguration(JHipsterProperties jHipsterProperties) {
this.jhipsterProperties = jHipsterProperties;
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
ResourceHandlerRegistration resourceHandlerRegistration = appendResourceHandler(registry);
initializeResourceHandler(resourceHandlerRegistration);
}
protected ResourceHandlerRegistration appendResourceHandler(ResourceHandlerRegistry registry) {
return registry.addResourceHandler(RESOURCE_PATHS);
}
protected void initializeResourceHandler(ResourceHandlerRegistration resourceHandlerRegistration) {
resourceHandlerRegistration.addResourceLocations(RESOURCE_LOCATIONS).setCacheControl(getCacheControl());
}
protected CacheControl getCacheControl() {
return CacheControl.maxAge(getJHipsterHttpCacheProperty(), TimeUnit.DAYS).cachePublic();
}
private int getJHipsterHttpCacheProperty() {
return jhipsterProperties.getHttp().getCache().getTimeToLiveInDays();
}
}

View File

@ -0,0 +1,98 @@
package org.gcube.isdashboard.config;
import static java.net.URLDecoder.decode;
import jakarta.servlet.*;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.server.*;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.env.Profiles;
import org.springframework.util.CollectionUtils;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import tech.jhipster.config.JHipsterConstants;
import tech.jhipster.config.JHipsterProperties;
/**
* Configuration of web application with Servlet 3.0 APIs.
*/
@Configuration
public class WebConfigurer implements ServletContextInitializer, WebServerFactoryCustomizer<WebServerFactory> {
private final Logger log = LoggerFactory.getLogger(WebConfigurer.class);
private final Environment env;
private final JHipsterProperties jHipsterProperties;
public WebConfigurer(Environment env, JHipsterProperties jHipsterProperties) {
this.env = env;
this.jHipsterProperties = jHipsterProperties;
}
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
if (env.getActiveProfiles().length != 0) {
log.info("Web application configuration, using profiles: {}", (Object[]) env.getActiveProfiles());
}
log.info("Web application fully configured");
}
/**
* Customize the Servlet engine: Mime types, the document root, the cache.
*/
@Override
public void customize(WebServerFactory server) {
// When running in an IDE or with ./mvnw spring-boot:run, set location of the static web assets.
setLocationForStaticAssets(server);
}
private void setLocationForStaticAssets(WebServerFactory server) {
if (server instanceof ConfigurableServletWebServerFactory servletWebServer) {
File root;
String prefixPath = resolvePathPrefix();
root = new File(prefixPath + "target/classes/static/");
if (root.exists() && root.isDirectory()) {
servletWebServer.setDocumentRoot(root);
}
}
}
/**
* Resolve path prefix to static resources.
*/
private String resolvePathPrefix() {
String fullExecutablePath = decode(this.getClass().getResource("").getPath(), StandardCharsets.UTF_8);
String rootPath = Paths.get(".").toUri().normalize().getPath();
String extractedPath = fullExecutablePath.replace(rootPath, "");
int extractionEndIndex = extractedPath.indexOf("target/");
if (extractionEndIndex <= 0) {
return "";
}
return extractedPath.substring(0, extractionEndIndex);
}
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = jHipsterProperties.getCors();
if (!CollectionUtils.isEmpty(config.getAllowedOrigins()) || !CollectionUtils.isEmpty(config.getAllowedOriginPatterns())) {
log.debug("Registering CORS filter");
source.registerCorsConfiguration("/api/**", config);
source.registerCorsConfiguration("/management/**", config);
source.registerCorsConfiguration("/v3/api-docs", config);
source.registerCorsConfiguration("/swagger-ui/**", config);
}
return new CorsFilter(source);
}
}

View File

@ -0,0 +1,4 @@
/**
* Application configuration.
*/
package org.gcube.isdashboard.config;

View File

@ -0,0 +1,4 @@
/**
* Application root.
*/
package org.gcube.isdashboard;

View File

@ -0,0 +1,15 @@
package org.gcube.isdashboard.security;
/**
* Constants for Spring Security authorities.
*/
public final class AuthoritiesConstants {
public static final String ADMIN = "ROLE_ADMIN";
public static final String USER = "ROLE_USER";
public static final String ANONYMOUS = "ROLE_ANONYMOUS";
private AuthoritiesConstants() {}
}

View File

@ -0,0 +1,86 @@
package org.gcube.isdashboard.security;
import java.util.Arrays;
import java.util.Optional;
import java.util.stream.Stream;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
/**
* Utility class for Spring Security.
*/
public final class SecurityUtils {
private SecurityUtils() {}
/**
* Get the login of the current user.
*
* @return the login of the current user.
*/
public static Optional<String> getCurrentUserLogin() {
SecurityContext securityContext = SecurityContextHolder.getContext();
return Optional.ofNullable(extractPrincipal(securityContext.getAuthentication()));
}
private static String extractPrincipal(Authentication authentication) {
if (authentication == null) {
return null;
} else if (authentication.getPrincipal() instanceof UserDetails springSecurityUser) {
return springSecurityUser.getUsername();
} else if (authentication.getPrincipal() instanceof String s) {
return s;
}
return null;
}
/**
* Check if a user is authenticated.
*
* @return true if the user is authenticated, false otherwise.
*/
public static boolean isAuthenticated() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return authentication != null && getAuthorities(authentication).noneMatch(AuthoritiesConstants.ANONYMOUS::equals);
}
/**
* Checks if the current user has any of the authorities.
*
* @param authorities the authorities to check.
* @return true if the current user has any of the authorities, false otherwise.
*/
public static boolean hasCurrentUserAnyOfAuthorities(String... authorities) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return (
authentication != null && getAuthorities(authentication).anyMatch(authority -> Arrays.asList(authorities).contains(authority))
);
}
/**
* Checks if the current user has none of the authorities.
*
* @param authorities the authorities to check.
* @return true if the current user has none of the authorities, false otherwise.
*/
public static boolean hasCurrentUserNoneOfAuthorities(String... authorities) {
return !hasCurrentUserAnyOfAuthorities(authorities);
}
/**
* Checks if the current user has a specific authority.
*
* @param authority the authority to check.
* @return true if the current user has the authority, false otherwise.
*/
public static boolean hasCurrentUserThisAuthority(String authority) {
return hasCurrentUserAnyOfAuthorities(authority);
}
private static Stream<String> getAuthorities(Authentication authentication) {
return authentication.getAuthorities().stream().map(GrantedAuthority::getAuthority);
}
}

View File

@ -0,0 +1,4 @@
/**
* Application security utilities.
*/
package org.gcube.isdashboard.security;

View File

@ -0,0 +1,32 @@
package org.gcube.isdashboard.web.filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import org.springframework.web.filter.OncePerRequestFilter;
public class SpaWebFilter extends OncePerRequestFilter {
/**
* Forwards any unmapped paths (except those containing a period) to the client {@code index.html}.
*/
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String path = request.getRequestURI();
if (
!path.startsWith("/api") &&
!path.startsWith("/management") &&
!path.startsWith("/v3/api-docs") &&
!path.contains(".") &&
path.matches("/(.*)")
) {
request.getRequestDispatcher("/index.html").forward(request, response);
return;
}
filterChain.doFilter(request, response);
}
}

View File

@ -0,0 +1,4 @@
/**
* Request chain filters.
*/
package org.gcube.isdashboard.web.filter;

View File

@ -0,0 +1,78 @@
package org.gcube.isdashboard.web.rest;
import com.fasterxml.jackson.annotation.JsonCreator;
import jakarta.servlet.http.HttpServletRequest;
import java.util.Set;
import java.util.stream.Collectors;
import org.gcube.isdashboard.security.SecurityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api")
public class AccountResource {
private final Logger log = LoggerFactory.getLogger(AccountResource.class);
private static class AccountResourceException extends RuntimeException {}
/**
* {@code GET /account} : get the current user.
*
* @return the current user.
* @throws AccountResourceException {@code 500 (Internal Server Error)} if the user couldn't be returned.
*/
@GetMapping("/account")
public UserVM getAccount() {
String login = SecurityUtils.getCurrentUserLogin().orElseThrow(AccountResourceException::new);
Set<String> authorities = SecurityContextHolder
.getContext()
.getAuthentication()
.getAuthorities()
.stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toSet());
return new UserVM(login, authorities);
}
/**
* {@code 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.
*/
@GetMapping("/authenticate")
public String isAuthenticated(HttpServletRequest request) {
log.debug("REST request to check if the current user is authenticated");
return request.getRemoteUser();
}
private static class UserVM {
private String login;
private Set<String> authorities;
@JsonCreator
UserVM(String login, Set<String> authorities) {
this.login = login;
this.authorities = authorities;
}
public boolean isActivated() {
return true;
}
public Set<String> getAuthorities() {
return authorities;
}
public String getLogin() {
return login;
}
}
}

View File

@ -0,0 +1,50 @@
package org.gcube.isdashboard.web.rest.errors;
import java.net.URI;
import org.springframework.http.HttpStatus;
import org.springframework.web.ErrorResponseException;
import tech.jhipster.web.rest.errors.ProblemDetailWithCause;
import tech.jhipster.web.rest.errors.ProblemDetailWithCause.ProblemDetailWithCauseBuilder;
@SuppressWarnings("java:S110") // Inheritance tree of classes should not be too deep
public class BadRequestAlertException extends ErrorResponseException {
private static final long serialVersionUID = 1L;
private final String entityName;
private final String errorKey;
public BadRequestAlertException(String defaultMessage, String entityName, String errorKey) {
this(ErrorConstants.DEFAULT_TYPE, defaultMessage, entityName, errorKey);
}
public BadRequestAlertException(URI type, String defaultMessage, String entityName, String errorKey) {
super(
HttpStatus.BAD_REQUEST,
ProblemDetailWithCauseBuilder
.instance()
.withStatus(HttpStatus.BAD_REQUEST.value())
.withType(type)
.withTitle(defaultMessage)
.withProperty("message", "error." + errorKey)
.withProperty("params", entityName)
.build(),
null
);
this.entityName = entityName;
this.errorKey = errorKey;
}
public String getEntityName() {
return entityName;
}
public String getErrorKey() {
return errorKey;
}
public ProblemDetailWithCause getProblemDetailWithCause() {
return (ProblemDetailWithCause) this.getBody();
}
}

View File

@ -0,0 +1,13 @@
package org.gcube.isdashboard.web.rest.errors;
import java.net.URI;
public final class ErrorConstants {
public static final String ERR_VALIDATION = "error.validation";
public static final String PROBLEM_BASE_URL = "https://www.jhipster.tech/problem";
public static final URI DEFAULT_TYPE = URI.create(PROBLEM_BASE_URL + "/problem-with-message");
public static final URI CONSTRAINT_VIOLATION_TYPE = URI.create(PROBLEM_BASE_URL + "/constraint-violation");
private ErrorConstants() {}
}

View File

@ -0,0 +1,253 @@
package org.gcube.isdashboard.web.rest.errors;
import static org.springframework.core.annotation.AnnotatedElementUtils.findMergedAnnotation;
import jakarta.servlet.http.HttpServletRequest;
import java.net.URI;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.lang.Nullable;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.web.ErrorResponse;
import org.springframework.web.ErrorResponseException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import tech.jhipster.config.JHipsterConstants;
import tech.jhipster.web.rest.errors.ProblemDetailWithCause;
import tech.jhipster.web.rest.errors.ProblemDetailWithCause.ProblemDetailWithCauseBuilder;
import tech.jhipster.web.util.HeaderUtil;
/**
* Controller advice to translate the server side exceptions to client-friendly json structures.
* The error response follows RFC7807 - Problem Details for HTTP APIs (https://tools.ietf.org/html/rfc7807).
*/
@ControllerAdvice
public class ExceptionTranslator extends ResponseEntityExceptionHandler {
private static final String FIELD_ERRORS_KEY = "fieldErrors";
private static final String MESSAGE_KEY = "message";
private static final String PATH_KEY = "path";
private static final boolean CASUAL_CHAIN_ENABLED = false;
@Value("${jhipster.clientApp.name}")
private String applicationName;
private final Environment env;
public ExceptionTranslator(Environment env) {
this.env = env;
}
@ExceptionHandler
public ResponseEntity<Object> handleAnyException(Throwable ex, NativeWebRequest request) {
ProblemDetailWithCause pdCause = wrapAndCustomizeProblem(ex, request);
return handleExceptionInternal(
(Exception) ex,
pdCause,
buildHeaders(ex, request),
HttpStatusCode.valueOf(pdCause.getStatus()),
request
);
}
@Nullable
@Override
protected ResponseEntity<Object> handleExceptionInternal(
Exception ex,
@Nullable Object body,
HttpHeaders headers,
HttpStatusCode statusCode,
WebRequest request
) {
body = body == null ? wrapAndCustomizeProblem((Throwable) ex, (NativeWebRequest) request) : body;
return super.handleExceptionInternal(ex, body, headers, statusCode, request);
}
protected ProblemDetailWithCause wrapAndCustomizeProblem(Throwable ex, NativeWebRequest request) {
return customizeProblem(getProblemDetailWithCause(ex), ex, request);
}
private ProblemDetailWithCause getProblemDetailWithCause(Throwable ex) {
if (
ex instanceof ErrorResponseException exp && exp.getBody() instanceof ProblemDetailWithCause
) return (ProblemDetailWithCause) exp.getBody();
return ProblemDetailWithCauseBuilder.instance().withStatus(toStatus(ex).value()).build();
}
protected ProblemDetailWithCause customizeProblem(ProblemDetailWithCause problem, Throwable err, NativeWebRequest request) {
if (problem.getStatus() <= 0) problem.setStatus(toStatus(err));
if (problem.getType() == null || problem.getType().equals(URI.create("about:blank"))) problem.setType(getMappedType(err));
// higher precedence to Custom/ResponseStatus types
String title = extractTitle(err, problem.getStatus());
String problemTitle = problem.getTitle();
if (problemTitle == null || !problemTitle.equals(title)) {
problem.setTitle(title);
}
if (problem.getDetail() == null) {
// higher precedence to cause
problem.setDetail(getCustomizedErrorDetails(err));
}
Map<String, Object> problemProperties = problem.getProperties();
if (problemProperties == null || !problemProperties.containsKey(MESSAGE_KEY)) problem.setProperty(
MESSAGE_KEY,
getMappedMessageKey(err) != null ? getMappedMessageKey(err) : "error.http." + problem.getStatus()
);
if (problemProperties == null || !problemProperties.containsKey(PATH_KEY)) problem.setProperty(PATH_KEY, getPathValue(request));
if (
(err instanceof MethodArgumentNotValidException) &&
(problemProperties == null || !problemProperties.containsKey(FIELD_ERRORS_KEY))
) problem.setProperty(FIELD_ERRORS_KEY, getFieldErrors((MethodArgumentNotValidException) err));
problem.setCause(buildCause(err.getCause(), request).orElse(null));
return problem;
}
private String extractTitle(Throwable err, int statusCode) {
return getCustomizedTitle(err) != null ? getCustomizedTitle(err) : extractTitleForResponseStatus(err, statusCode);
}
private List<FieldErrorVM> getFieldErrors(MethodArgumentNotValidException ex) {
return ex
.getBindingResult()
.getFieldErrors()
.stream()
.map(f ->
new FieldErrorVM(
f.getObjectName().replaceFirst("DTO$", ""),
f.getField(),
StringUtils.isNotBlank(f.getDefaultMessage()) ? f.getDefaultMessage() : f.getCode()
)
)
.toList();
}
private String extractTitleForResponseStatus(Throwable err, int statusCode) {
ResponseStatus specialStatus = extractResponseStatus(err);
return specialStatus == null ? HttpStatus.valueOf(statusCode).getReasonPhrase() : specialStatus.reason();
}
private String extractURI(NativeWebRequest request) {
HttpServletRequest nativeRequest = request.getNativeRequest(HttpServletRequest.class);
return nativeRequest != null ? nativeRequest.getRequestURI() : StringUtils.EMPTY;
}
private HttpStatus toStatus(final Throwable throwable) {
// Let the ErrorResponse take this responsibility
if (throwable instanceof ErrorResponse err) return HttpStatus.valueOf(err.getBody().getStatus());
return Optional
.ofNullable(getMappedStatus(throwable))
.orElse(
Optional.ofNullable(resolveResponseStatus(throwable)).map(ResponseStatus::value).orElse(HttpStatus.INTERNAL_SERVER_ERROR)
);
}
private ResponseStatus extractResponseStatus(final Throwable throwable) {
return Optional.ofNullable(resolveResponseStatus(throwable)).orElse(null);
}
private ResponseStatus resolveResponseStatus(final Throwable type) {
final ResponseStatus candidate = findMergedAnnotation(type.getClass(), ResponseStatus.class);
return candidate == null && type.getCause() != null ? resolveResponseStatus(type.getCause()) : candidate;
}
private URI getMappedType(Throwable err) {
if (err instanceof MethodArgumentNotValidException exp) return ErrorConstants.CONSTRAINT_VIOLATION_TYPE;
return ErrorConstants.DEFAULT_TYPE;
}
private String getMappedMessageKey(Throwable err) {
if (err instanceof MethodArgumentNotValidException) return ErrorConstants.ERR_VALIDATION;
return null;
}
private String getCustomizedTitle(Throwable err) {
if (err instanceof MethodArgumentNotValidException exp) return "Method argument not valid";
return null;
}
private String getCustomizedErrorDetails(Throwable err) {
Collection<String> activeProfiles = Arrays.asList(env.getActiveProfiles());
if (activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_PRODUCTION)) {
if (err instanceof HttpMessageConversionException) return "Unable to convert http message";
if (containsPackageName(err.getMessage())) return "Unexpected runtime exception";
}
return err.getCause() != null ? err.getCause().getMessage() : err.getMessage();
}
private HttpStatus getMappedStatus(Throwable err) {
// Where we disagree with Spring defaults
if (err instanceof AccessDeniedException) return HttpStatus.FORBIDDEN;
if (err instanceof BadCredentialsException) return HttpStatus.UNAUTHORIZED;
return null;
}
private URI getPathValue(NativeWebRequest request) {
if (request == null) return URI.create("about:blank");
return URI.create(extractURI(request));
}
private HttpHeaders buildHeaders(Throwable err, NativeWebRequest request) {
return err instanceof BadRequestAlertException
? HeaderUtil.createFailureAlert(
applicationName,
true,
((BadRequestAlertException) err).getEntityName(),
((BadRequestAlertException) err).getErrorKey(),
((BadRequestAlertException) err).getMessage()
)
: null;
}
public Optional<ProblemDetailWithCause> buildCause(final Throwable throwable, NativeWebRequest request) {
if (throwable != null && isCasualChainEnabled()) {
return Optional.of(customizeProblem(getProblemDetailWithCause(throwable), throwable, request));
}
return Optional.ofNullable(null);
}
private boolean isCasualChainEnabled() {
// Customize as per the needs
return CASUAL_CHAIN_ENABLED;
}
private boolean containsPackageName(String message) {
// This list is for sure not complete
return StringUtils.containsAny(
message,
"org.",
"java.",
"net.",
"jakarta.",
"javax.",
"com.",
"io.",
"de.",
"org.gcube.isdashboard"
);
}
}

View File

@ -0,0 +1,32 @@
package org.gcube.isdashboard.web.rest.errors;
import java.io.Serializable;
public class FieldErrorVM implements Serializable {
private static final long serialVersionUID = 1L;
private final String objectName;
private final String field;
private final String message;
public FieldErrorVM(String dto, String field, String message) {
this.objectName = dto;
this.field = field;
this.message = message;
}
public String getObjectName() {
return objectName;
}
public String getField() {
return field;
}
public String getMessage() {
return message;
}
}

View File

@ -0,0 +1,4 @@
/**
* Rest layer error handling.
*/
package org.gcube.isdashboard.web.rest.errors;

View File

@ -0,0 +1,4 @@
/**
* Rest layer.
*/
package org.gcube.isdashboard.web.rest;

View File

@ -0,0 +1,10 @@
${AnsiColor.GREEN} ██╗${AnsiColor.RED} ██╗ ██╗ ████████╗ ███████╗ ██████╗ ████████╗ ████████╗ ███████╗
${AnsiColor.GREEN} ██║${AnsiColor.RED} ██║ ██║ ╚══██╔══╝ ██╔═══██╗ ██╔════╝ ╚══██╔══╝ ██╔═════╝ ██╔═══██╗
${AnsiColor.GREEN} ██║${AnsiColor.RED} ████████║ ██║ ███████╔╝ ╚█████╗ ██║ ██████╗ ███████╔╝
${AnsiColor.GREEN}██╗ ██║${AnsiColor.RED} ██╔═══██║ ██║ ██╔════╝ ╚═══██╗ ██║ ██╔═══╝ ██╔══██║
${AnsiColor.GREEN}╚██████╔╝${AnsiColor.RED} ██║ ██║ ████████╗ ██║ ██████╔╝ ██║ ████████╗ ██║ ╚██╗
${AnsiColor.GREEN} ╚═════╝ ${AnsiColor.RED} ╚═╝ ╚═╝ ╚═══════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══════╝ ╚═╝ ╚═╝
${AnsiColor.BRIGHT_BLUE}:: JHipster 🤓 :: Running Spring Boot ${spring-boot.version} :: Startup profile(s) ${spring.profiles.active} ::
:: https://www.jhipster.tech ::${AnsiColor.DEFAULT}

View File

@ -0,0 +1,83 @@
# ===================================================================
# Spring Boot configuration for the "dev" profile.
#
# This configuration overrides the application.yml file.
#
# More information on profiles: https://www.jhipster.tech/profiles/
# More information on configuration properties: https://www.jhipster.tech/common-application-properties/
# ===================================================================
# ===================================================================
# Standard Spring Boot properties.
# Full reference is available at:
# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
# ===================================================================
logging:
level:
ROOT: DEBUG
tech.jhipster: DEBUG
org.hibernate.SQL: DEBUG
org.gcube.isdashboard: DEBUG
spring:
devtools:
restart:
enabled: true
additional-exclude: static/**
livereload:
enabled: false # we use Webpack dev server + BrowserSync for livereload
jackson:
serialization:
indent-output: true
messages:
cache-duration: PT1S # 1 second, see the ISO 8601 standard
thymeleaf:
cache: false
server:
port: 8080
# ===================================================================
# JHipster specific properties
#
# Full reference is available at: https://www.jhipster.tech/common-application-properties/
# ===================================================================
jhipster:
cache: # Cache configuration
ehcache: # Ehcache configuration
time-to-live-seconds: 3600 # By default objects stay 1 hour in the cache
max-entries: 100 # Number of objects in each cache entry
# CORS is only enabled by default with the "dev" profile
cors:
# Allow Ionic for JHipster by default (* no longer allowed in Spring Boot 2.4+)
allowed-origins: 'http://localhost:8100,https://localhost:8100,http://localhost:9000,https://localhost:9000,http://localhost:4200,https://localhost:4200'
# Enable CORS when running in GitHub Codespaces
allowed-origin-patterns: 'https://*.githubpreview.dev'
allowed-methods: '*'
allowed-headers: '*'
exposed-headers: 'Link,X-Total-Count,X-${jhipster.clientApp.name}-alert,X-${jhipster.clientApp.name}-error,X-${jhipster.clientApp.name}-params'
allow-credentials: true
max-age: 1800
security:
remember-me:
# security key (this key should be unique for your application, and kept secret)
key: 6038bcb93c3f8cc61c163e71aa55573477e46c3535cf0624212567f396b89e7be83d90b03885a818d14b1b7f4928ab548561
logging:
use-json-format: false # By default, logs are not in Json format
logstash: # Forward logs to logstash over a socket, used by LoggingConfiguration
enabled: false
host: localhost
port: 5000
ring-buffer-size: 512
# ===================================================================
# Application specific properties
# Add your own application properties here, see the ApplicationProperties class
# to have type-safe configuration, like in the JHipsterProperties above
#
# More documentation is available at:
# https://www.jhipster.tech/common-application-properties/
# ===================================================================
# application:

View File

@ -0,0 +1,98 @@
# ===================================================================
# Spring Boot configuration for the "prod" profile.
#
# This configuration overrides the application.yml file.
#
# More information on profiles: https://www.jhipster.tech/profiles/
# More information on configuration properties: https://www.jhipster.tech/common-application-properties/
# ===================================================================
# ===================================================================
# Standard Spring Boot properties.
# Full reference is available at:
# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
# ===================================================================
logging:
level:
ROOT: INFO
tech.jhipster: INFO
org.gcube.isdashboard: INFO
management:
prometheus:
metrics:
export:
enabled: false
spring:
devtools:
restart:
enabled: false
livereload:
enabled: false
thymeleaf:
cache: true
# ===================================================================
# To enable TLS in production, generate a certificate using:
# keytool -genkey -alias isdashboard -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 3650
#
# You can also use Let's Encrypt:
# See details in topic "Create a Java Keystore (.JKS) from Let's Encrypt Certificates" on https://maximilian-boehm.com/en-gb/blog
#
# Then, modify the server.ssl properties so your "server" configuration looks like:
#
# server:
# port: 443
# ssl:
# key-store: classpath:config/tls/keystore.p12
# key-store-password: password
# key-store-type: PKCS12
# key-alias: selfsigned
# # The ciphers suite enforce the security by deactivating some old and deprecated SSL cipher, this list was tested against SSL Labs (https://www.ssllabs.com/ssltest/)
# ciphers: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 ,TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 ,TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 ,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,TLS_DHE_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA,TLS_RSA_WITH_CAMELLIA_256_CBC_SHA,TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA,TLS_RSA_WITH_CAMELLIA_128_CBC_SHA
# ===================================================================
server:
port: 8080
shutdown: graceful # see https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-graceful-shutdown
compression:
enabled: true
mime-types: text/html,text/xml,text/plain,text/css,application/javascript,application/json,image/svg+xml
min-response-size: 1024
# ===================================================================
# JHipster specific properties
#
# Full reference is available at: https://www.jhipster.tech/common-application-properties/
# ===================================================================
jhipster:
http:
cache: # Used by the CachingHttpHeadersFilter
timeToLiveInDays: 1461
cache: # Cache configuration
ehcache: # Ehcache configuration
time-to-live-seconds: 3600 # By default objects stay 1 hour in the cache
max-entries: 1000 # Number of objects in each cache entry
security:
remember-me:
# security key (this key should be unique for your application, and kept secret)
key: 6038bcb93c3f8cc61c163e71aa55573477e46c3535cf0624212567f396b89e7be83d90b03885a818d14b1b7f4928ab548561
logging:
use-json-format: false # By default, logs are not in Json format
logstash: # Forward logs to logstash over a socket, used by LoggingConfiguration
enabled: false
host: localhost
port: 5000
ring-buffer-size: 512
# ===================================================================
# Application specific properties
# Add your own application properties here, see the ApplicationProperties class
# to have type-safe configuration, like in the JHipsterProperties above
#
# More documentation is available at:
# https://www.jhipster.tech/common-application-properties/
# ===================================================================
# application:

View File

@ -0,0 +1,19 @@
# ===================================================================
# Activate this profile to enable TLS and HTTP/2.
#
# JHipster has generated a self-signed certificate, which will be used to encrypt traffic.
# As your browser will not understand this certificate, you will need to import it.
#
# Another (easiest) solution with Chrome is to enable the "allow-insecure-localhost" flag
# at chrome://flags/#allow-insecure-localhost
# ===================================================================
server:
ssl:
key-store: classpath:config/tls/keystore.p12
key-store-password: password
key-store-type: PKCS12
key-alias: selfsigned
ciphers: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
enabled-protocols: TLSv1.2
http2:
enabled: true

View File

@ -0,0 +1,192 @@
# ===================================================================
# Spring Boot configuration.
#
# This configuration will be overridden by the Spring profile you use,
# for example application-dev.yml if you use the "dev" profile.
#
# More information on profiles: https://www.jhipster.tech/profiles/
# More information on configuration properties: https://www.jhipster.tech/common-application-properties/
# ===================================================================
# ===================================================================
# Standard Spring Boot properties.
# Full reference is available at:
# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
# ===================================================================
---
# Conditionally disable springdoc on missing api-docs profile
spring:
config:
activate:
on-profile: '!api-docs'
springdoc:
api-docs:
enabled: false
---
management:
endpoints:
web:
base-path: /management
exposure:
include:
- configprops
- env
- health
- info
- jhimetrics
- jhiopenapigroups
- logfile
- loggers
- prometheus
- threaddump
- caches
endpoint:
health:
show-details: when_authorized
roles: 'ROLE_ADMIN'
probes:
enabled: true
group:
liveness:
include: livenessState
readiness:
include: readinessState
jhimetrics:
enabled: true
info:
git:
mode: full
env:
enabled: true
health:
mail:
enabled: false # When using the MailService, configure an SMTP server and set this to true
prometheus:
metrics:
export:
enabled: true
step: 60
enable:
http: true
jvm: true
logback: true
process: true
system: true
distribution:
percentiles-histogram:
all: true
percentiles:
all: 0, 0.5, 0.75, 0.95, 0.99, 1.0
tags:
application: ${spring.application.name}
web:
server:
request:
autotime:
enabled: true
spring:
application:
name: isdashboard
profiles:
# The commented value for `active` can be replaced with valid Spring profiles to load.
# Otherwise, it will be filled in by maven when building the JAR file
# Either way, it can be overridden by `--spring.profiles.active` value passed in the commandline or `-Dspring.profiles.active` set in `JAVA_OPTS`
active: #spring.profiles.active#
group:
dev:
- dev
- api-docs
# Uncomment to activate TLS for the dev profile
#- tls
jmx:
enabled: false
messages:
basename: i18n/messages
main:
allow-bean-definition-overriding: true
mvc:
problemdetails:
enabled: true
security:
user:
name: admin
password: admin
roles:
- ADMIN
- USER
task:
execution:
thread-name-prefix: isdashboard-task-
pool:
core-size: 2
max-size: 50
queue-capacity: 10000
scheduling:
thread-name-prefix: isdashboard-scheduling-
pool:
size: 2
thymeleaf:
mode: HTML
output:
ansi:
console-available: true
server:
servlet:
session:
cookie:
http-only: true
springdoc:
show-actuator: true
# Properties to be exposed on the /info management endpoint
info:
# Comma separated list of profiles that will trigger the ribbon to show
display-ribbon-on-profiles: 'dev'
# ===================================================================
# JHipster specific properties
#
# Full reference is available at: https://www.jhipster.tech/common-application-properties/
# ===================================================================
jhipster:
clientApp:
name: 'isdashboardApp'
# By default CORS is disabled. Uncomment to enable.
# cors:
# allowed-origins: "http://localhost:8100,http://localhost:9000"
# allowed-methods: "*"
# allowed-headers: "*"
# exposed-headers: "Link,X-Total-Count,X-${jhipster.clientApp.name}-alert,X-${jhipster.clientApp.name}-error,X-${jhipster.clientApp.name}-params"
# allow-credentials: true
# max-age: 1800
mail:
from: isdashboard@localhost
api-docs:
default-include-pattern: ${server.servlet.context-path:}/api/**
management-include-pattern: ${server.servlet.context-path:}/management/**
title: Isdashboard API
description: Isdashboard API documentation
version: 0.0.1
terms-of-service-url:
contact-name:
contact-url:
contact-email:
license: unlicensed
license-url:
security:
content-security-policy: "default-src 'self'; frame-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://storage.googleapis.com; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:"
# ===================================================================
# Application specific properties
# Add your own application properties here, see the ApplicationProperties class
# to have type-safe configuration, like in the JHipsterProperties above
#
# More documentation is available at:
# https://www.jhipster.tech/common-application-properties/
# ===================================================================
# application:

Binary file not shown.

View File

@ -0,0 +1,6 @@
# Error page
error.title=Your request cannot be processed
error.subtitle=Sorry, an error has occurred.
error.status=Status:
error.message=Message:

View File

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration>
<configuration scan="true">
<!-- Patterns based on https://github.com/spring-projects/spring-boot/blob/v3.0.0/spring-boot-project/spring-boot/src/main/resources/org/springframework/boot/logging/logback/defaults.xml -->
<conversionRule conversionWord="crlf" converterClass="org.gcube.isdashboard.config.CRLFLogConverter" />
<property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd'T'HH:mm:ss.SSSXXX}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %crlf(%m){red} %n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<!-- The FILE and ASYNC appenders are here as examples for a production configuration -->
<!--
<property name="FILE_LOG_PATTERN" value="${FILE_LOG_PATTERN:-%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd'T'HH:mm:ss.SSSXXX}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } &#45;&#45;&#45; [%t] %-40.40logger{39} : %crlf(%m) %n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
-->
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml" />
<!-- The FILE and ASYNC appenders are here as examples for a production configuration -->
<!--
<include resource="org/springframework/boot/logging/logback/file-appender.xml" />
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<queueSize>512</queueSize>
<appender-ref ref="FILE"/>
</appender>
<root level="${logging.level.root}">
<appender-ref ref="ASYNC"/>
</root>
-->
<logger name="angus.activation" level="WARN"/>
<logger name="jakarta.activation" level="WARN"/>
<logger name="jakarta.mail" level="WARN"/>
<logger name="jakarta.management.remote" level="WARN"/>
<logger name="jakarta.xml.bind" level="WARN"/>
<logger name="jdk.event.security" level="INFO"/>
<logger name="com.ryantenney" level="WARN"/>
<logger name="com.sun" level="WARN"/>
<logger name="com.zaxxer" level="WARN"/>
<logger name="io.undertow" level="WARN"/>
<logger name="io.undertow.websockets.jsr" level="ERROR"/>
<logger name="org.ehcache" level="WARN"/>
<logger name="org.apache" level="WARN"/>
<logger name="org.apache.catalina.startup.DigesterFactory" level="OFF"/>
<logger name="org.bson" level="WARN"/>
<logger name="org.hibernate.validator" level="WARN"/>
<logger name="org.springframework" level="WARN"/>
<logger name="org.springframework.web" level="WARN"/>
<logger name="org.springframework.security" level="WARN"/>
<logger name="org.springframework.boot.autoconfigure.logging" level="INFO"/>
<logger name="org.springframework.cache" level="WARN"/>
<logger name="org.thymeleaf" level="WARN"/>
<logger name="org.xnio" level="WARN"/>
<logger name="io.swagger.v3" level="INFO"/>
<logger name="sun.rmi" level="WARN"/>
<logger name="sun.rmi.transport" level="WARN"/>
<!-- See https://github.com/jhipster/generator-jhipster/issues/13835 -->
<logger name="Validator" level="INFO"/>
<!-- See https://github.com/jhipster/generator-jhipster/issues/14444 -->
<logger name="_org.springframework.web.servlet.HandlerMapping.Mappings" level="INFO"/>
<!-- jhipster-needle-logback-add-log - JHipster will add a new log with level -->
<springProperty name="log.level" source="logging.level.root" defaultValue="INFO" />
<root level="${log.level}">
<appender-ref ref="CONSOLE" />
</root>
<contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
<resetJUL>true</resetJUL>
</contextListener>
</configuration>

View File

@ -0,0 +1,92 @@
<!DOCTYPE html>
<html
xmlns:th="http://www.thymeleaf.org"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.thymeleaf.org"
th:lang="${#locale.language}"
lang="en"
>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="icon" href="${baseUrl}/favicon.ico" />
<title>Your request cannot be processed</title>
<style>
::-moz-selection {
background: #b3d4fc;
text-shadow: none;
}
::selection {
background: #b3d4fc;
text-shadow: none;
}
html {
padding: 30px 10px;
font-size: 20px;
line-height: 1.4;
color: #737373;
background: #3e8acc;
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
}
html,
input {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
body {
max-width: 1000px;
_width: 500px;
padding: 30px 20px 50px;
border: 1px solid #b3b3b3;
border-radius: 4px;
margin: 0 auto;
box-shadow: 0 1px 10px #a7a7a7, inset 0 1px 0 #fff;
background: #fcfcfc;
}
h1 {
margin: 0 10px;
font-size: 50px;
text-align: center;
}
h1 span {
color: #bbb;
}
h3 {
margin: 1.5em 0 0.5em;
}
p {
margin: 1em 0;
}
ul {
padding: 0 0 0 40px;
margin: 1em 0;
}
.container {
max-width: 800px;
_width: 380px;
margin: 0 auto;
}
</style>
</head>
<body>
<div class="container">
<h1 th:text="#{error.title}">Your request cannot be processed <span>:(</span></h1>
<p th:text="#{error.subtitle}">Sorry, an error has occurred.</p>
<span th:text="#{error.status}">Status:</span>&nbsp;<span th:text="${error}"></span>&nbsp;(<span th:text="${error}"></span>)<br />
<span th:if="${!#strings.isEmpty(message)}">
<span th:text="#{error.message}">Message:</span>&nbsp;<span th:text="${message}"></span><br />
</span>
</div>
</body>
</html>

58
src/main/webapp/404.html Normal file
View File

@ -0,0 +1,58 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Page Not Found</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="favicon.ico" />
<style>
* {
line-height: 1.2;
margin: 0;
}
html {
color: #888;
display: table;
font-family: sans-serif;
height: 100%;
text-align: center;
width: 100%;
}
body {
display: table-cell;
vertical-align: middle;
margin: 2em auto;
}
h1 {
color: #555;
font-size: 2em;
font-weight: 400;
}
p {
margin: 0 auto;
width: 280px;
}
@media only screen and (max-width: 280px) {
body,
p {
width: 95%;
}
h1 {
font-size: 1.5em;
margin: 0 0 0.3em;
}
}
</style>
</head>
<body>
<h1>Page Not Found</h1>
<p>Sorry, but the page you were trying to view does not exist.</p>
</body>
</html>
<!-- IE needs 512+ bytes: http://blogs.msdn.com/b/ieinternals/archive/2010/08/19/http-error-pages-in-internet-explorer.aspx -->

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<mime-mapping>
<extension>html</extension>
<mime-type>text/html;charset=utf-8</mime-type>
</mime-mapping>
</web-app>

View File

@ -0,0 +1,18 @@
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
/* jhipster-needle-add-admin-module-import - JHipster will add admin modules imports here */
@NgModule({
imports: [
/* jhipster-needle-add-admin-module - JHipster will add admin modules here */
RouterModule.forChild([
{
path: 'docs',
loadComponent: () => import('./docs/docs.component'),
title: 'API',
},
/* jhipster-needle-add-admin-route - JHipster will add admin routes here */
]),
],
})
export default class AdminRoutingModule {}

View File

@ -0,0 +1,10 @@
<iframe
src="swagger-ui/index.html"
width="100%"
height="900"
seamless
target="_top"
title="Swagger UI"
class="border-0"
data-cy="swagger-frame"
></iframe>

View File

@ -0,0 +1,6 @@
@import 'bootstrap/scss/functions';
@import 'bootstrap/scss/variables';
iframe {
background: white;
}

View File

@ -0,0 +1,9 @@
import { Component } from '@angular/core';
@Component({
standalone: true,
selector: 'jhi-docs',
templateUrl: './docs.component.html',
styleUrls: ['./docs.component.scss'],
})
export default class DocsComponent {}

View File

@ -0,0 +1,17 @@
import { Injectable } from '@angular/core';
import { RouterStateSnapshot, TitleStrategy } from '@angular/router';
@Injectable()
export class AppPageTitleStrategy extends TitleStrategy {
constructor() {
super();
}
override updateTitle(routerState: RouterStateSnapshot): void {
let pageTitle = this.buildTitle(routerState);
if (!pageTitle) {
pageTitle = 'Isdashboard';
}
document.title = pageTitle;
}
}

View File

@ -0,0 +1,52 @@
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { errorRoute } from './layouts/error/error.route';
import { DEBUG_INFO_ENABLED } from 'app/app.constants';
import { Authority } from 'app/config/authority.constants';
import HomeComponent from './home/home.component';
import NavbarComponent from './layouts/navbar/navbar.component';
import LoginComponent from './login/login.component';
import { UserRouteAccessService } from 'app/core/auth/user-route-access.service';
@NgModule({
imports: [
RouterModule.forRoot(
[
{
path: '',
component: HomeComponent,
title: 'Welcome, Java Hipster!',
},
{
path: '',
component: NavbarComponent,
outlet: 'navbar',
},
{
path: 'admin',
data: {
authorities: [Authority.ADMIN],
},
canActivate: [UserRouteAccessService],
loadChildren: () => import('./admin/admin-routing.module'),
},
{
path: 'login',
component: LoginComponent,
title: 'Sign in',
},
{
path: '',
loadChildren: () => import(`./entities/entity-routing.module`).then(({ EntityRoutingModule }) => EntityRoutingModule),
},
...errorRoute,
],
{ enableTracing: DEBUG_INFO_ENABLED, bindToComponentInputs: true }
),
],
exports: [RouterModule],
})
export class AppRoutingModule {}

View File

@ -0,0 +1,9 @@
// These constants are injected via webpack DefinePlugin variables.
// You can add more variables in webpack.common.js or in profile specific webpack.<dev|prod>.js files.
// If you change the values in the webpack config files, you need to re run webpack to update the application
declare const __DEBUG_INFO_ENABLED__: boolean;
declare const __VERSION__: string;
export const VERSION = __VERSION__;
export const DEBUG_INFO_ENABLED = __DEBUG_INFO_ENABLED__;

View File

@ -0,0 +1,49 @@
import { NgModule, LOCALE_ID } from '@angular/core';
import { registerLocaleData } from '@angular/common';
import { HttpClientModule } from '@angular/common/http';
import locale from '@angular/common/locales/en';
import { BrowserModule, Title } from '@angular/platform-browser';
import { TitleStrategy } from '@angular/router';
import { ServiceWorkerModule } from '@angular/service-worker';
import { FaIconLibrary } from '@fortawesome/angular-fontawesome';
import dayjs from 'dayjs/esm';
import { NgbDateAdapter, NgbDatepickerConfig } from '@ng-bootstrap/ng-bootstrap';
import { ApplicationConfigService } from 'app/core/config/application-config.service';
import './config/dayjs';
import { AppRoutingModule } from './app-routing.module';
// jhipster-needle-angular-add-module-import JHipster will add new module here
import { NgbDateDayjsAdapter } from './config/datepicker-adapter';
import { fontAwesomeIcons } from './config/font-awesome-icons';
import { httpInterceptorProviders } from 'app/core/interceptor/index';
import MainComponent from './layouts/main/main.component';
import MainModule from './layouts/main/main.module';
import { AppPageTitleStrategy } from './app-page-title-strategy';
@NgModule({
imports: [
BrowserModule,
// jhipster-needle-angular-add-module JHipster will add new module here
AppRoutingModule,
// Set this to true to enable service worker (PWA)
ServiceWorkerModule.register('ngsw-worker.js', { enabled: false }),
HttpClientModule,
MainModule,
],
providers: [
Title,
{ provide: LOCALE_ID, useValue: 'en' },
{ provide: NgbDateAdapter, useClass: NgbDateDayjsAdapter },
httpInterceptorProviders,
{ provide: TitleStrategy, useClass: AppPageTitleStrategy },
],
bootstrap: [MainComponent],
})
export class AppModule {
constructor(applicationConfigService: ApplicationConfigService, iconLibrary: FaIconLibrary, dpConfig: NgbDatepickerConfig) {
applicationConfigService.setEndpointPrefix(SERVER_API_URL);
registerLocaleData(locale);
iconLibrary.addIcons(...fontAwesomeIcons);
dpConfig.minDate = { year: dayjs().subtract(100, 'year').year(), month: 1, day: 1 };
}
}

View File

@ -0,0 +1,4 @@
export enum Authority {
ADMIN = 'ROLE_ADMIN',
USER = 'ROLE_USER',
}

View File

@ -0,0 +1,20 @@
/**
* Angular bootstrap Date adapter
*/
import { Injectable } from '@angular/core';
import { NgbDateAdapter, NgbDateStruct } from '@ng-bootstrap/ng-bootstrap';
import dayjs from 'dayjs/esm';
@Injectable()
export class NgbDateDayjsAdapter extends NgbDateAdapter<dayjs.Dayjs> {
fromModel(date: dayjs.Dayjs | null): NgbDateStruct | null {
if (date && dayjs.isDayjs(date) && date.isValid()) {
return { year: date.year(), month: date.month() + 1, day: date.date() };
}
return null;
}
toModel(date: NgbDateStruct | null): dayjs.Dayjs | null {
return date ? dayjs(`${date.year}-${date.month}-${date.day}`) : null;
}
}

View File

@ -0,0 +1,11 @@
import dayjs from 'dayjs/esm';
import customParseFormat from 'dayjs/esm/plugin/customParseFormat';
import duration from 'dayjs/esm/plugin/duration';
import relativeTime from 'dayjs/esm/plugin/relativeTime';
// jhipster-needle-i18n-language-dayjs-imports - JHipster will import languages from dayjs here
// DAYJS CONFIGURATION
dayjs.extend(customParseFormat);
dayjs.extend(duration);
dayjs.extend(relativeTime);

View File

@ -0,0 +1,3 @@
export const PROBLEM_BASE_URL = 'https://www.jhipster.tech/problem';
export const EMAIL_ALREADY_USED_TYPE = `${PROBLEM_BASE_URL}/email-already-used`;
export const LOGIN_ALREADY_USED_TYPE = `${PROBLEM_BASE_URL}/login-already-used`;

View File

@ -0,0 +1,83 @@
import {
faArrowLeft,
faAsterisk,
faBan,
faBars,
faBell,
faBook,
faCalendarAlt,
faCheck,
faCloud,
faCogs,
faDatabase,
faEye,
faFlag,
faHeart,
faHome,
faList,
faLock,
faPencilAlt,
faPlus,
faRoad,
faSave,
faSearch,
faSignOutAlt,
faSignInAlt,
faSort,
faSortDown,
faSortUp,
faSync,
faTachometerAlt,
faTasks,
faThList,
faTimes,
faTrashAlt,
faUser,
faUserPlus,
faUsers,
faUsersCog,
faWrench,
// jhipster-needle-add-icon-import
} from '@fortawesome/free-solid-svg-icons';
export const fontAwesomeIcons = [
faArrowLeft,
faAsterisk,
faBan,
faBars,
faBell,
faBook,
faCalendarAlt,
faCheck,
faCloud,
faCogs,
faDatabase,
faEye,
faFlag,
faHeart,
faHome,
faList,
faLock,
faPencilAlt,
faPlus,
faRoad,
faSave,
faSearch,
faSignOutAlt,
faSignInAlt,
faSort,
faSortDown,
faSortUp,
faSync,
faTachometerAlt,
faTasks,
faThList,
faTimes,
faTrashAlt,
faUser,
faUserPlus,
faUsers,
faUsersCog,
faWrench,
// jhipster-needle-add-icon-import
];

View File

@ -0,0 +1,2 @@
export const DATE_FORMAT = 'YYYY-MM-DD';
export const DATE_TIME_FORMAT = 'YYYY-MM-DDTHH:mm';

View File

@ -0,0 +1,5 @@
export const ASC = 'asc';
export const DESC = 'desc';
export const SORT = 'sort';
export const ITEM_DELETED_EVENT = 'deleted';
export const DEFAULT_SORT_DATA = 'defaultSort';

View File

@ -0,0 +1,3 @@
export const TOTAL_COUNT_RESPONSE_HEADER = 'X-Total-Count';
export const PAGE_HEADER = 'page';
export const ITEMS_PER_PAGE = 20;

View File

@ -0,0 +1,14 @@
import { Injectable } from '@angular/core';
import { NgbPaginationConfig } from '@ng-bootstrap/ng-bootstrap';
import { ITEMS_PER_PAGE } from 'app/config/pagination.constants';
@Injectable({ providedIn: 'root' })
export class PaginationConfig {
constructor(config: NgbPaginationConfig) {
config.boundaryLinks = true;
config.maxSize = 5;
config.pageSize = ITEMS_PER_PAGE;
config.size = 'sm';
}
}

View File

@ -0,0 +1,12 @@
export class Account {
constructor(
public activated: boolean,
public authorities: string[],
public email: string,
public firstName: string | null,
public langKey: string,
public lastName: string | null,
public login: string,
public imageUrl: string | null
) {}
}

View File

@ -0,0 +1,198 @@
jest.mock('app/core/auth/state-storage.service');
import { Router } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { TestBed } from '@angular/core/testing';
import { Account } from 'app/core/auth/account.model';
import { Authority } from 'app/config/authority.constants';
import { StateStorageService } from 'app/core/auth/state-storage.service';
import { AccountService } from './account.service';
function accountWithAuthorities(authorities: string[]): Account {
return {
activated: true,
authorities,
email: '',
firstName: '',
langKey: '',
lastName: '',
login: '',
imageUrl: '',
};
}
describe('Account Service', () => {
let service: AccountService;
let httpMock: HttpTestingController;
let mockStorageService: StateStorageService;
let mockRouter: Router;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule, RouterTestingModule.withRoutes([])],
providers: [StateStorageService],
});
service = TestBed.inject(AccountService);
httpMock = TestBed.inject(HttpTestingController);
mockStorageService = TestBed.inject(StateStorageService);
mockRouter = TestBed.inject(Router);
jest.spyOn(mockRouter, 'navigateByUrl').mockImplementation(() => Promise.resolve(true));
});
afterEach(() => {
httpMock.verify();
});
describe('authenticate', () => {
it('authenticationState should emit null if input is null', () => {
// GIVEN
let userIdentity: Account | null = accountWithAuthorities([]);
service.getAuthenticationState().subscribe(account => (userIdentity = account));
// WHEN
service.authenticate(null);
// THEN
expect(userIdentity).toBeNull();
expect(service.isAuthenticated()).toBe(false);
});
it('authenticationState should emit the same account as was in input parameter', () => {
// GIVEN
const expectedResult = accountWithAuthorities([]);
let userIdentity: Account | null = null;
service.getAuthenticationState().subscribe(account => (userIdentity = account));
// WHEN
service.authenticate(expectedResult);
// THEN
expect(userIdentity).toEqual(expectedResult);
expect(service.isAuthenticated()).toBe(true);
});
});
describe('identity', () => {
it('should call /account only once if last call have not returned', () => {
// When I call
service.identity().subscribe();
// Once more
service.identity().subscribe();
// Then there is only request
httpMock.expectOne({ method: 'GET' });
});
it('should call /account only once if not logged out after first authentication and should call /account again if user has logged out', () => {
// Given the user is authenticated
service.identity().subscribe();
httpMock.expectOne({ method: 'GET' }).flush({});
// When I call
service.identity().subscribe();
// Then there is no second request
httpMock.expectNone({ method: 'GET' });
// When I log out
service.authenticate(null);
// and then call
service.identity().subscribe();
// Then there is a new request
httpMock.expectOne({ method: 'GET' });
});
describe('navigateToStoredUrl', () => {
it('should navigate to the previous stored url post successful authentication', () => {
// GIVEN
mockStorageService.getUrl = jest.fn(() => 'admin/users?page=0');
// WHEN
service.identity().subscribe();
httpMock.expectOne({ method: 'GET' }).flush({});
// THEN
expect(mockStorageService.getUrl).toHaveBeenCalledTimes(1);
expect(mockStorageService.clearUrl).toHaveBeenCalledTimes(1);
expect(mockRouter.navigateByUrl).toHaveBeenCalledWith('admin/users?page=0');
});
it('should not navigate to the previous stored url when authentication fails', () => {
// WHEN
service.identity().subscribe();
httpMock.expectOne({ method: 'GET' }).error(new ErrorEvent(''));
// THEN
expect(mockStorageService.getUrl).not.toHaveBeenCalled();
expect(mockStorageService.clearUrl).not.toHaveBeenCalled();
expect(mockRouter.navigateByUrl).not.toHaveBeenCalled();
});
it('should not navigate to the previous stored url when no such url exists post successful authentication', () => {
// GIVEN
mockStorageService.getUrl = jest.fn(() => null);
// WHEN
service.identity().subscribe();
httpMock.expectOne({ method: 'GET' }).flush({});
// THEN
expect(mockStorageService.getUrl).toHaveBeenCalledTimes(1);
expect(mockStorageService.clearUrl).not.toHaveBeenCalled();
expect(mockRouter.navigateByUrl).not.toHaveBeenCalled();
});
});
});
describe('hasAnyAuthority', () => {
describe('hasAnyAuthority string parameter', () => {
it('should return false if user is not logged', () => {
const hasAuthority = service.hasAnyAuthority(Authority.USER);
expect(hasAuthority).toBe(false);
});
it('should return false if user is logged and has not authority', () => {
service.authenticate(accountWithAuthorities([Authority.USER]));
const hasAuthority = service.hasAnyAuthority(Authority.ADMIN);
expect(hasAuthority).toBe(false);
});
it('should return true if user is logged and has authority', () => {
service.authenticate(accountWithAuthorities([Authority.USER]));
const hasAuthority = service.hasAnyAuthority(Authority.USER);
expect(hasAuthority).toBe(true);
});
});
describe('hasAnyAuthority array parameter', () => {
it('should return false if user is not logged', () => {
const hasAuthority = service.hasAnyAuthority([Authority.USER]);
expect(hasAuthority).toBeFalsy();
});
it('should return false if user is logged and has not authority', () => {
service.authenticate(accountWithAuthorities([Authority.USER]));
const hasAuthority = service.hasAnyAuthority([Authority.ADMIN]);
expect(hasAuthority).toBe(false);
});
it('should return true if user is logged and has authority', () => {
service.authenticate(accountWithAuthorities([Authority.USER]));
const hasAuthority = service.hasAnyAuthority([Authority.USER, Authority.ADMIN]);
expect(hasAuthority).toBe(true);
});
});
});
});

View File

@ -0,0 +1,77 @@
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { HttpClient } from '@angular/common/http';
import { Observable, ReplaySubject, of } from 'rxjs';
import { shareReplay, tap, catchError } from 'rxjs/operators';
import { StateStorageService } from 'app/core/auth/state-storage.service';
import { ApplicationConfigService } from '../config/application-config.service';
import { Account } from 'app/core/auth/account.model';
@Injectable({ providedIn: 'root' })
export class AccountService {
private userIdentity: Account | null = null;
private authenticationState = new ReplaySubject<Account | null>(1);
private accountCache$?: Observable<Account> | null;
constructor(
private http: HttpClient,
private stateStorageService: StateStorageService,
private router: Router,
private applicationConfigService: ApplicationConfigService
) {}
authenticate(identity: Account | null): void {
this.userIdentity = identity;
this.authenticationState.next(this.userIdentity);
if (!identity) {
this.accountCache$ = null;
}
}
hasAnyAuthority(authorities: string[] | string): boolean {
if (!this.userIdentity) {
return false;
}
if (!Array.isArray(authorities)) {
authorities = [authorities];
}
return this.userIdentity.authorities.some((authority: string) => authorities.includes(authority));
}
identity(force?: boolean): Observable<Account | null> {
if (!this.accountCache$ || force) {
this.accountCache$ = this.fetch().pipe(
tap((account: Account) => {
this.authenticate(account);
this.navigateToStoredUrl();
}),
shareReplay()
);
}
return this.accountCache$.pipe(catchError(() => of(null)));
}
isAuthenticated(): boolean {
return this.userIdentity !== null;
}
getAuthenticationState(): Observable<Account | null> {
return this.authenticationState.asObservable();
}
private fetch(): Observable<Account> {
return this.http.get<Account>(this.applicationConfigService.getEndpointFor('api/account'));
}
private navigateToStoredUrl(): void {
// previousState can be set in the authExpiredInterceptor and in the userRouteAccessService
// if login is successful, go to stored previousState and clear previousState
const previousUrl = this.stateStorageService.getUrl();
if (previousUrl) {
this.stateStorageService.clearUrl();
this.router.navigateByUrl(previousUrl);
}
}
}

View File

@ -0,0 +1,34 @@
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { ApplicationConfigService } from '../config/application-config.service';
import { Login } from 'app/login/login.model';
@Injectable({ providedIn: 'root' })
export class AuthServerProvider {
constructor(private http: HttpClient, private applicationConfigService: ApplicationConfigService) {}
login(credentials: Login): Observable<{}> {
const data =
`username=${encodeURIComponent(credentials.username)}` +
`&password=${encodeURIComponent(credentials.password)}` +
`&remember-me=${credentials.rememberMe ? 'true' : 'false'}` +
'&submit=Login';
const headers = new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded');
return this.http.post(this.applicationConfigService.getEndpointFor('api/authentication'), data, { headers });
}
logout(): Observable<void> {
// logout from the server
return this.http.post(this.applicationConfigService.getEndpointFor('api/logout'), {}).pipe(
map(() => {
// to get a new csrf token call the api
this.http.get(this.applicationConfigService.getEndpointFor('api/account')).subscribe();
})
);
}
}

View File

@ -0,0 +1,40 @@
import { Injectable } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class StateStorageService {
private previousUrlKey = 'previousUrl';
private authenticationKey = 'jhi-authenticationToken';
storeUrl(url: string): void {
sessionStorage.setItem(this.previousUrlKey, JSON.stringify(url));
}
getUrl(): string | null {
const previousUrl = sessionStorage.getItem(this.previousUrlKey);
return previousUrl ? (JSON.parse(previousUrl) as string | null) : previousUrl;
}
clearUrl(): void {
sessionStorage.removeItem(this.previousUrlKey);
}
storeAuthenticationToken(authenticationToken: string, rememberMe: boolean): void {
authenticationToken = JSON.stringify(authenticationToken);
this.clearAuthenticationToken();
if (rememberMe) {
localStorage.setItem(this.authenticationKey, authenticationToken);
} else {
sessionStorage.setItem(this.authenticationKey, authenticationToken);
}
}
getAuthenticationToken(): string | null {
const authenticationToken = localStorage.getItem(this.authenticationKey) ?? sessionStorage.getItem(this.authenticationKey);
return authenticationToken ? (JSON.parse(authenticationToken) as string | null) : authenticationToken;
}
clearAuthenticationToken(): void {
sessionStorage.removeItem(this.authenticationKey);
localStorage.removeItem(this.authenticationKey);
}
}

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