├── .devcontainer ├── Dockerfile └── devcontainer.json ├── .editorconfig ├── .gitattributes ├── .gitignore ├── .husky └── pre-commit ├── .jhipster └── BankAccount.json ├── .lintstagedrc.cjs ├── .mvn ├── jvm.config └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── .prettierignore ├── .prettierrc ├── .yo-rc.json ├── LICENSE.txt ├── README.md ├── angular.json ├── checkstyle.xml ├── cypress-audits.config.ts ├── cypress.config.ts ├── eslint.config.mjs ├── jest.conf.js ├── mvnw ├── mvnw.cmd ├── ngsw-config.json ├── npmw ├── npmw.cmd ├── package.json ├── pom.xml ├── sonar-project.properties ├── src ├── main │ ├── docker │ │ ├── app.yml │ │ ├── central-server-config │ │ │ ├── README.md │ │ │ └── application.yml │ │ ├── config │ │ │ ├── git2consul.json │ │ │ └── mysql │ │ │ │ └── my.cnf │ │ ├── consul.yml │ │ ├── grafana │ │ │ └── provisioning │ │ │ │ ├── dashboards │ │ │ │ ├── JVM.json │ │ │ │ └── dashboard.yml │ │ │ │ └── datasources │ │ │ │ └── datasource.yml │ │ ├── jhipster-control-center.yml │ │ ├── jib │ │ │ └── entrypoint.sh │ │ ├── monitoring.yml │ │ ├── mysql.yml │ │ ├── prometheus │ │ │ └── prometheus.yml │ │ ├── services.yml │ │ ├── sonar.yml │ │ └── zipkin.yml │ ├── java │ │ └── io │ │ │ └── github │ │ │ └── jhipster │ │ │ └── sample │ │ │ ├── GeneratedByJHipster.java │ │ │ ├── JhipsterSampleGatewayApp.java │ │ │ ├── aop │ │ │ └── logging │ │ │ │ ├── LoggingAspect.java │ │ │ │ └── package-info.java │ │ │ ├── config │ │ │ ├── ApplicationProperties.java │ │ │ ├── AsyncConfiguration.java │ │ │ ├── CRLFLogConverter.java │ │ │ ├── Constants.java │ │ │ ├── DatabaseConfiguration.java │ │ │ ├── DateTimeFormatConfiguration.java │ │ │ ├── JacksonConfiguration.java │ │ │ ├── LiquibaseConfiguration.java │ │ │ ├── LoggingAspectConfiguration.java │ │ │ ├── LoggingConfiguration.java │ │ │ ├── ReactorConfiguration.java │ │ │ ├── SecurityConfiguration.java │ │ │ ├── SecurityJwtConfiguration.java │ │ │ ├── WebConfigurer.java │ │ │ └── package-info.java │ │ │ ├── domain │ │ │ ├── AbstractAuditingEntity.java │ │ │ ├── Authority.java │ │ │ ├── AuthorityCallback.java │ │ │ ├── User.java │ │ │ └── package-info.java │ │ │ ├── management │ │ │ ├── SecurityMetersService.java │ │ │ └── package-info.java │ │ │ ├── package-info.java │ │ │ ├── repository │ │ │ ├── AuthorityRepository.java │ │ │ ├── EntityManager.java │ │ │ ├── UserRepository.java │ │ │ ├── UserSqlHelper.java │ │ │ ├── package-info.java │ │ │ └── rowmapper │ │ │ │ ├── ColumnConverter.java │ │ │ │ ├── UserRowMapper.java │ │ │ │ └── package-info.java │ │ │ ├── security │ │ │ ├── AuthoritiesConstants.java │ │ │ ├── DomainUserDetailsService.java │ │ │ ├── SecurityUtils.java │ │ │ ├── UserNotActivatedException.java │ │ │ ├── jwt │ │ │ │ ├── JWTRelayGatewayFilterFactory.java │ │ │ │ └── package-info.java │ │ │ └── package-info.java │ │ │ ├── service │ │ │ ├── EmailAlreadyUsedException.java │ │ │ ├── InvalidPasswordException.java │ │ │ ├── MailService.java │ │ │ ├── UserService.java │ │ │ ├── UsernameAlreadyUsedException.java │ │ │ ├── dto │ │ │ │ ├── AdminUserDTO.java │ │ │ │ ├── PasswordChangeDTO.java │ │ │ │ ├── UserDTO.java │ │ │ │ └── package-info.java │ │ │ ├── mapper │ │ │ │ ├── UserMapper.java │ │ │ │ └── package-info.java │ │ │ └── package-info.java │ │ │ └── web │ │ │ ├── filter │ │ │ ├── SpaWebFilter.java │ │ │ └── package-info.java │ │ │ └── rest │ │ │ ├── AccountResource.java │ │ │ ├── AuthenticateController.java │ │ │ ├── AuthorityResource.java │ │ │ ├── GatewayResource.java │ │ │ ├── PublicUserResource.java │ │ │ ├── UserResource.java │ │ │ ├── errors │ │ │ ├── BadRequestAlertException.java │ │ │ ├── EmailAlreadyUsedException.java │ │ │ ├── ErrorConstants.java │ │ │ ├── ExceptionTranslator.java │ │ │ ├── FieldErrorVM.java │ │ │ ├── InvalidPasswordException.java │ │ │ ├── LoginAlreadyUsedException.java │ │ │ └── package-info.java │ │ │ ├── package-info.java │ │ │ └── vm │ │ │ ├── KeyAndPasswordVM.java │ │ │ ├── LoginVM.java │ │ │ ├── ManagedUserVM.java │ │ │ ├── RouteVM.java │ │ │ └── package-info.java │ ├── resources │ │ ├── .h2.server.properties │ │ ├── banner.txt │ │ ├── config │ │ │ ├── application-dev.yml │ │ │ ├── application-prod.yml │ │ │ ├── application-tls.yml │ │ │ ├── application.yml │ │ │ ├── bootstrap-prod.yml │ │ │ ├── bootstrap.yml │ │ │ ├── liquibase │ │ │ │ ├── changelog │ │ │ │ │ └── 00000000000000_initial_schema.xml │ │ │ │ ├── data │ │ │ │ │ ├── authority.csv │ │ │ │ │ ├── user.csv │ │ │ │ │ └── user_authority.csv │ │ │ │ └── master.xml │ │ │ └── tls │ │ │ │ └── keystore.p12 │ │ ├── i18n │ │ │ └── messages.properties │ │ ├── logback-spring.xml │ │ └── templates │ │ │ ├── error.html │ │ │ └── mail │ │ │ ├── activationEmail.html │ │ │ ├── creationEmail.html │ │ │ └── passwordResetEmail.html │ └── webapp │ │ ├── 404.html │ │ ├── WEB-INF │ │ └── web.xml │ │ ├── app │ │ ├── account │ │ │ ├── account.route.ts │ │ │ ├── activate │ │ │ │ ├── activate.component.html │ │ │ │ ├── activate.component.spec.ts │ │ │ │ ├── activate.component.ts │ │ │ │ ├── activate.route.ts │ │ │ │ ├── activate.service.spec.ts │ │ │ │ └── activate.service.ts │ │ │ ├── password-reset │ │ │ │ ├── finish │ │ │ │ │ ├── password-reset-finish.component.html │ │ │ │ │ ├── password-reset-finish.component.spec.ts │ │ │ │ │ ├── password-reset-finish.component.ts │ │ │ │ │ ├── password-reset-finish.route.ts │ │ │ │ │ ├── password-reset-finish.service.spec.ts │ │ │ │ │ └── password-reset-finish.service.ts │ │ │ │ └── init │ │ │ │ │ ├── password-reset-init.component.html │ │ │ │ │ ├── password-reset-init.component.spec.ts │ │ │ │ │ ├── password-reset-init.component.ts │ │ │ │ │ ├── password-reset-init.route.ts │ │ │ │ │ ├── password-reset-init.service.spec.ts │ │ │ │ │ └── password-reset-init.service.ts │ │ │ ├── password │ │ │ │ ├── password-strength-bar │ │ │ │ │ ├── password-strength-bar.component.html │ │ │ │ │ ├── password-strength-bar.component.scss │ │ │ │ │ ├── password-strength-bar.component.spec.ts │ │ │ │ │ └── password-strength-bar.component.ts │ │ │ │ ├── password.component.html │ │ │ │ ├── password.component.spec.ts │ │ │ │ ├── password.component.ts │ │ │ │ ├── password.route.ts │ │ │ │ ├── password.service.spec.ts │ │ │ │ └── password.service.ts │ │ │ ├── register │ │ │ │ ├── register.component.html │ │ │ │ ├── register.component.spec.ts │ │ │ │ ├── register.component.ts │ │ │ │ ├── register.model.ts │ │ │ │ ├── register.route.ts │ │ │ │ ├── register.service.spec.ts │ │ │ │ └── register.service.ts │ │ │ └── settings │ │ │ │ ├── settings.component.html │ │ │ │ ├── settings.component.spec.ts │ │ │ │ ├── settings.component.ts │ │ │ │ └── settings.route.ts │ │ ├── admin │ │ │ ├── admin.routes.ts │ │ │ ├── configuration │ │ │ │ ├── configuration.component.html │ │ │ │ ├── configuration.component.spec.ts │ │ │ │ ├── configuration.component.ts │ │ │ │ ├── configuration.model.ts │ │ │ │ ├── configuration.service.spec.ts │ │ │ │ └── configuration.service.ts │ │ │ ├── docs │ │ │ │ ├── docs.component.html │ │ │ │ ├── docs.component.scss │ │ │ │ └── docs.component.ts │ │ │ ├── gateway │ │ │ │ ├── gateway-route.model.ts │ │ │ │ ├── gateway-routes.service.ts │ │ │ │ ├── gateway.component.html │ │ │ │ └── gateway.component.ts │ │ │ ├── health │ │ │ │ ├── health.component.html │ │ │ │ ├── health.component.spec.ts │ │ │ │ ├── health.component.ts │ │ │ │ ├── health.model.ts │ │ │ │ ├── health.service.spec.ts │ │ │ │ ├── health.service.ts │ │ │ │ └── modal │ │ │ │ │ ├── health-modal.component.html │ │ │ │ │ ├── health-modal.component.spec.ts │ │ │ │ │ └── health-modal.component.ts │ │ │ ├── logs │ │ │ │ ├── log.model.ts │ │ │ │ ├── logs.component.html │ │ │ │ ├── logs.component.spec.ts │ │ │ │ ├── logs.component.ts │ │ │ │ ├── logs.service.spec.ts │ │ │ │ └── logs.service.ts │ │ │ ├── metrics │ │ │ │ ├── blocks │ │ │ │ │ ├── jvm-memory │ │ │ │ │ │ ├── jvm-memory.component.html │ │ │ │ │ │ └── jvm-memory.component.ts │ │ │ │ │ ├── jvm-threads │ │ │ │ │ │ ├── jvm-threads.component.html │ │ │ │ │ │ └── jvm-threads.component.ts │ │ │ │ │ ├── metrics-cache │ │ │ │ │ │ ├── metrics-cache.component.html │ │ │ │ │ │ └── metrics-cache.component.ts │ │ │ │ │ ├── metrics-datasource │ │ │ │ │ │ ├── metrics-datasource.component.html │ │ │ │ │ │ └── metrics-datasource.component.ts │ │ │ │ │ ├── metrics-endpoints-requests │ │ │ │ │ │ ├── metrics-endpoints-requests.component.html │ │ │ │ │ │ └── metrics-endpoints-requests.component.ts │ │ │ │ │ ├── metrics-garbagecollector │ │ │ │ │ │ ├── metrics-garbagecollector.component.html │ │ │ │ │ │ └── metrics-garbagecollector.component.ts │ │ │ │ │ ├── metrics-modal-threads │ │ │ │ │ │ ├── metrics-modal-threads.component.html │ │ │ │ │ │ ├── metrics-modal-threads.component.spec.ts │ │ │ │ │ │ └── metrics-modal-threads.component.ts │ │ │ │ │ ├── metrics-request │ │ │ │ │ │ ├── metrics-request.component.html │ │ │ │ │ │ └── metrics-request.component.ts │ │ │ │ │ └── metrics-system │ │ │ │ │ │ ├── metrics-system.component.html │ │ │ │ │ │ └── metrics-system.component.ts │ │ │ │ ├── metrics.component.html │ │ │ │ ├── metrics.component.spec.ts │ │ │ │ ├── metrics.component.ts │ │ │ │ ├── metrics.model.ts │ │ │ │ ├── metrics.service.spec.ts │ │ │ │ └── metrics.service.ts │ │ │ └── user-management │ │ │ │ ├── delete │ │ │ │ ├── user-management-delete-dialog.component.html │ │ │ │ ├── user-management-delete-dialog.component.spec.ts │ │ │ │ └── user-management-delete-dialog.component.ts │ │ │ │ ├── detail │ │ │ │ ├── user-management-detail.component.html │ │ │ │ ├── user-management-detail.component.spec.ts │ │ │ │ └── user-management-detail.component.ts │ │ │ │ ├── list │ │ │ │ ├── user-management.component.html │ │ │ │ ├── user-management.component.spec.ts │ │ │ │ └── user-management.component.ts │ │ │ │ ├── service │ │ │ │ ├── user-management.service.spec.ts │ │ │ │ └── user-management.service.ts │ │ │ │ ├── update │ │ │ │ ├── user-management-update.component.html │ │ │ │ ├── user-management-update.component.spec.ts │ │ │ │ └── user-management-update.component.ts │ │ │ │ ├── user-management.model.ts │ │ │ │ └── user-management.route.ts │ │ ├── app-page-title-strategy.ts │ │ ├── app.component.ts │ │ ├── app.config.ts │ │ ├── app.routes.ts │ │ ├── config │ │ │ ├── authority.constants.ts │ │ │ ├── datepicker-adapter.ts │ │ │ ├── dayjs.ts │ │ │ ├── error.constants.ts │ │ │ ├── font-awesome-icons.ts │ │ │ ├── input.constants.ts │ │ │ ├── navigation.constants.ts │ │ │ ├── pagination.constants.ts │ │ │ └── uib-pagination.config.ts │ │ ├── core │ │ │ ├── auth │ │ │ │ ├── account.model.ts │ │ │ │ ├── account.service.spec.ts │ │ │ │ ├── account.service.ts │ │ │ │ ├── auth-jwt.service.spec.ts │ │ │ │ ├── auth-jwt.service.ts │ │ │ │ ├── state-storage.service.ts │ │ │ │ └── user-route-access.service.ts │ │ │ ├── config │ │ │ │ ├── application-config.service.spec.ts │ │ │ │ └── application-config.service.ts │ │ │ ├── interceptor │ │ │ │ ├── auth-expired.interceptor.ts │ │ │ │ ├── auth.interceptor.ts │ │ │ │ ├── error-handler.interceptor.ts │ │ │ │ ├── index.ts │ │ │ │ └── notification.interceptor.ts │ │ │ ├── request │ │ │ │ ├── request-util.ts │ │ │ │ └── request.model.ts │ │ │ └── util │ │ │ │ ├── alert.service.spec.ts │ │ │ │ ├── alert.service.ts │ │ │ │ ├── data-util.service.spec.ts │ │ │ │ ├── data-util.service.ts │ │ │ │ ├── event-manager.service.spec.ts │ │ │ │ ├── event-manager.service.ts │ │ │ │ ├── operators.spec.ts │ │ │ │ ├── operators.ts │ │ │ │ ├── parse-links.service.spec.ts │ │ │ │ └── parse-links.service.ts │ │ ├── entities │ │ │ ├── admin │ │ │ │ └── authority │ │ │ │ │ ├── authority.model.ts │ │ │ │ │ ├── authority.routes.ts │ │ │ │ │ ├── authority.test-samples.ts │ │ │ │ │ ├── delete │ │ │ │ │ ├── authority-delete-dialog.component.html │ │ │ │ │ ├── authority-delete-dialog.component.spec.ts │ │ │ │ │ └── authority-delete-dialog.component.ts │ │ │ │ │ ├── detail │ │ │ │ │ ├── authority-detail.component.html │ │ │ │ │ ├── authority-detail.component.spec.ts │ │ │ │ │ └── authority-detail.component.ts │ │ │ │ │ ├── list │ │ │ │ │ ├── authority.component.html │ │ │ │ │ ├── authority.component.spec.ts │ │ │ │ │ └── authority.component.ts │ │ │ │ │ ├── route │ │ │ │ │ ├── authority-routing-resolve.service.spec.ts │ │ │ │ │ └── authority-routing-resolve.service.ts │ │ │ │ │ ├── service │ │ │ │ │ ├── authority.service.spec.ts │ │ │ │ │ └── authority.service.ts │ │ │ │ │ └── update │ │ │ │ │ ├── authority-form.service.spec.ts │ │ │ │ │ ├── authority-form.service.ts │ │ │ │ │ ├── authority-update.component.html │ │ │ │ │ ├── authority-update.component.spec.ts │ │ │ │ │ └── authority-update.component.ts │ │ │ ├── entity-navbar-items.ts │ │ │ ├── entity.routes.ts │ │ │ ├── jhipsterSampleMicroservice │ │ │ │ └── bank-account │ │ │ │ │ ├── bank-account.model.ts │ │ │ │ │ ├── bank-account.routes.ts │ │ │ │ │ ├── bank-account.test-samples.ts │ │ │ │ │ ├── delete │ │ │ │ │ ├── bank-account-delete-dialog.component.html │ │ │ │ │ ├── bank-account-delete-dialog.component.spec.ts │ │ │ │ │ └── bank-account-delete-dialog.component.ts │ │ │ │ │ ├── detail │ │ │ │ │ ├── bank-account-detail.component.html │ │ │ │ │ ├── bank-account-detail.component.spec.ts │ │ │ │ │ └── bank-account-detail.component.ts │ │ │ │ │ ├── list │ │ │ │ │ ├── bank-account.component.html │ │ │ │ │ ├── bank-account.component.spec.ts │ │ │ │ │ └── bank-account.component.ts │ │ │ │ │ ├── route │ │ │ │ │ ├── bank-account-routing-resolve.service.spec.ts │ │ │ │ │ └── bank-account-routing-resolve.service.ts │ │ │ │ │ ├── service │ │ │ │ │ ├── bank-account.service.spec.ts │ │ │ │ │ └── bank-account.service.ts │ │ │ │ │ └── update │ │ │ │ │ ├── bank-account-form.service.spec.ts │ │ │ │ │ ├── bank-account-form.service.ts │ │ │ │ │ ├── bank-account-update.component.html │ │ │ │ │ ├── bank-account-update.component.spec.ts │ │ │ │ │ └── bank-account-update.component.ts │ │ │ └── user │ │ │ │ ├── service │ │ │ │ ├── user.service.spec.ts │ │ │ │ └── user.service.ts │ │ │ │ ├── user.model.ts │ │ │ │ └── user.test-samples.ts │ │ ├── home │ │ │ ├── home.component.html │ │ │ ├── home.component.scss │ │ │ ├── home.component.spec.ts │ │ │ └── home.component.ts │ │ ├── layouts │ │ │ ├── error │ │ │ │ ├── error.component.html │ │ │ │ ├── error.component.ts │ │ │ │ └── error.route.ts │ │ │ ├── footer │ │ │ │ ├── footer.component.html │ │ │ │ └── footer.component.ts │ │ │ ├── main │ │ │ │ ├── main.component.html │ │ │ │ ├── main.component.spec.ts │ │ │ │ └── main.component.ts │ │ │ ├── navbar │ │ │ │ ├── navbar-item.model.d.ts │ │ │ │ ├── navbar.component.html │ │ │ │ ├── navbar.component.scss │ │ │ │ ├── navbar.component.spec.ts │ │ │ │ └── navbar.component.ts │ │ │ └── profiles │ │ │ │ ├── page-ribbon.component.scss │ │ │ │ ├── page-ribbon.component.spec.ts │ │ │ │ ├── page-ribbon.component.ts │ │ │ │ ├── profile-info.model.ts │ │ │ │ └── profile.service.ts │ │ ├── login │ │ │ ├── login.component.html │ │ │ ├── login.component.spec.ts │ │ │ ├── login.component.ts │ │ │ ├── login.model.ts │ │ │ └── login.service.ts │ │ └── shared │ │ │ ├── alert │ │ │ ├── alert-error.component.html │ │ │ ├── alert-error.component.spec.ts │ │ │ ├── alert-error.component.ts │ │ │ ├── alert-error.model.ts │ │ │ ├── alert.component.html │ │ │ ├── alert.component.spec.ts │ │ │ └── alert.component.ts │ │ │ ├── auth │ │ │ ├── has-any-authority.directive.spec.ts │ │ │ └── has-any-authority.directive.ts │ │ │ ├── date │ │ │ ├── duration.pipe.ts │ │ │ ├── format-medium-date.pipe.spec.ts │ │ │ ├── format-medium-date.pipe.ts │ │ │ ├── format-medium-datetime.pipe.spec.ts │ │ │ ├── format-medium-datetime.pipe.ts │ │ │ └── index.ts │ │ │ ├── filter │ │ │ ├── filter.component.html │ │ │ ├── filter.component.ts │ │ │ ├── filter.model.spec.ts │ │ │ ├── filter.model.ts │ │ │ └── index.ts │ │ │ ├── pagination │ │ │ ├── index.ts │ │ │ ├── item-count.component.spec.ts │ │ │ └── item-count.component.ts │ │ │ ├── shared.module.ts │ │ │ └── sort │ │ │ ├── index.ts │ │ │ ├── sort-by.directive.spec.ts │ │ │ ├── sort-by.directive.ts │ │ │ ├── sort-state.ts │ │ │ ├── sort.directive.spec.ts │ │ │ ├── sort.directive.ts │ │ │ ├── sort.service.spec.ts │ │ │ └── sort.service.ts │ │ ├── bootstrap.ts │ │ ├── content │ │ ├── css │ │ │ └── loading.css │ │ ├── images │ │ │ ├── jhipster_family_member_0.svg │ │ │ ├── jhipster_family_member_0_head-192.png │ │ │ ├── jhipster_family_member_0_head-256.png │ │ │ ├── jhipster_family_member_0_head-384.png │ │ │ ├── jhipster_family_member_0_head-512.png │ │ │ ├── jhipster_family_member_1.svg │ │ │ ├── jhipster_family_member_1_head-192.png │ │ │ ├── jhipster_family_member_1_head-256.png │ │ │ ├── jhipster_family_member_1_head-384.png │ │ │ ├── jhipster_family_member_1_head-512.png │ │ │ ├── jhipster_family_member_2.svg │ │ │ ├── jhipster_family_member_2_head-192.png │ │ │ ├── jhipster_family_member_2_head-256.png │ │ │ ├── jhipster_family_member_2_head-384.png │ │ │ ├── jhipster_family_member_2_head-512.png │ │ │ ├── jhipster_family_member_3.svg │ │ │ ├── jhipster_family_member_3_head-192.png │ │ │ ├── jhipster_family_member_3_head-256.png │ │ │ ├── jhipster_family_member_3_head-384.png │ │ │ ├── jhipster_family_member_3_head-512.png │ │ │ └── logo-jhipster.png │ │ └── scss │ │ │ ├── _bootstrap-variables.scss │ │ │ ├── global.scss │ │ │ └── vendor.scss │ │ ├── declarations.d.ts │ │ ├── environments │ │ ├── environment.development.ts │ │ └── environment.ts │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── main.ts │ │ ├── manifest.webapp │ │ ├── robots.txt │ │ └── swagger-ui │ │ └── index.html └── test │ ├── gatling │ └── conf │ │ ├── gatling.conf │ │ └── logback.xml │ ├── java │ └── io │ │ └── github │ │ └── jhipster │ │ └── sample │ │ ├── IntegrationTest.java │ │ ├── TechnicalStructureTest.java │ │ ├── config │ │ ├── AsyncSyncConfiguration.java │ │ ├── EmbeddedSQL.java │ │ ├── JHipsterBlockHoundIntegration.java │ │ ├── MysqlTestContainer.java │ │ ├── SpringBootTestClassOrderer.java │ │ ├── SqlTestContainer.java │ │ └── SqlTestContainersSpringContextCustomizerFactory.java │ │ ├── domain │ │ ├── AssertUtils.java │ │ ├── AuthorityAsserts.java │ │ ├── AuthorityTest.java │ │ └── AuthorityTestSamples.java │ │ ├── management │ │ └── SecurityMetersServiceTests.java │ │ ├── security │ │ ├── DomainUserDetailsServiceIT.java │ │ ├── SecurityUtilsUnitTest.java │ │ └── jwt │ │ │ ├── AuthenticationIntegrationTest.java │ │ │ ├── JwtAuthenticationTestUtils.java │ │ │ ├── TokenAuthenticationIT.java │ │ │ └── TokenAuthenticationSecurityMetersIT.java │ │ ├── service │ │ ├── MailServiceIT.java │ │ ├── UserServiceIT.java │ │ └── mapper │ │ │ └── UserMapperTest.java │ │ └── web │ │ ├── filter │ │ ├── SpaWebFilterIT.java │ │ └── SpaWebFilterTestController.java │ │ └── rest │ │ ├── AccountResourceIT.java │ │ ├── AuthenticateControllerIT.java │ │ ├── AuthorityResourceIT.java │ │ ├── PublicUserResourceIT.java │ │ ├── TestUtil.java │ │ ├── UserResourceIT.java │ │ ├── WithUnauthenticatedMockUser.java │ │ └── errors │ │ ├── ExceptionTranslatorIT.java │ │ └── ExceptionTranslatorTestController.java │ ├── javascript │ └── cypress │ │ ├── e2e │ │ ├── account │ │ │ ├── login-page.cy.ts │ │ │ ├── logout.cy.ts │ │ │ ├── password-page.cy.ts │ │ │ ├── register-page.cy.ts │ │ │ ├── reset-password-page.cy.ts │ │ │ └── settings-page.cy.ts │ │ ├── administration │ │ │ └── administration.cy.ts │ │ ├── entity │ │ │ ├── authority.cy.ts │ │ │ └── bank-account.cy.ts │ │ └── lighthouse.audits.ts │ │ ├── fixtures │ │ └── integration-test.png │ │ ├── plugins │ │ └── index.ts │ │ ├── support │ │ ├── account.ts │ │ ├── commands.ts │ │ ├── entity.ts │ │ ├── index.ts │ │ ├── management.ts │ │ └── navbar.ts │ │ └── tsconfig.json │ └── resources │ ├── META-INF │ ├── services │ │ └── reactor.blockhound.integration.BlockHoundIntegration │ └── spring.factories │ ├── config │ ├── application-testdev.yml │ ├── application.yml │ └── bootstrap.yml │ ├── i18n │ └── messages_en.properties │ ├── junit-platform.properties │ ├── logback.xml │ └── templates │ └── mail │ ├── activationEmail.html │ ├── creationEmail.html │ ├── passwordResetEmail.html │ └── testEmail.html ├── tsconfig.app.json ├── tsconfig.json ├── tsconfig.spec.json └── webpack ├── environment.js ├── logo-jhipster.png ├── proxy.conf.js └── webpack.custom.js /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.209.6/containers/java/.devcontainer/base.Dockerfile 2 | 3 | # [Choice] Java version (use -bullseye variants on local arm64/Apple Silicon): 17, 17-bullseye, 17-buster 4 | ARG VARIANT="17" 5 | FROM mcr.microsoft.com/vscode/devcontainers/java:0-${VARIANT} 6 | 7 | # [Option] Install Maven 8 | ARG INSTALL_MAVEN="false" 9 | ARG MAVEN_VERSION="" 10 | # [Option] Install Gradle 11 | ARG INSTALL_GRADLE="false" 12 | ARG GRADLE_VERSION="" 13 | RUN if [ "${INSTALL_MAVEN}" = "true" ]; then su vscode -c "umask 0002 && . /usr/local/sdkman/bin/sdkman-init.sh && sdk install maven \"${MAVEN_VERSION}\""; fi \ 14 | && if [ "${INSTALL_GRADLE}" = "true" ]; then su vscode -c "umask 0002 && . /usr/local/sdkman/bin/sdkman-init.sh && sdk install gradle \"${GRADLE_VERSION}\""; fi 15 | 16 | # [Choice] Node.js version: none, lts/*, 16, 14, 12, 10 17 | ARG NODE_VERSION="none" 18 | RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi 19 | 20 | # [Optional] Uncomment this section to install additional OS packages. 21 | # RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 22 | # && apt-get -y install --no-install-recommends 23 | 24 | # [Optional] Uncomment this line to install global node packages. 25 | # RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g " 2>&1 26 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | 9 | # We recommend you to keep these unchanged 10 | end_of_line = lf 11 | charset = utf-8 12 | trim_trailing_whitespace = true 13 | insert_final_newline = true 14 | 15 | # Change these settings to your own preference 16 | indent_style = space 17 | indent_size = 2 18 | 19 | [*.md] 20 | trim_trailing_whitespace = false 21 | 22 | # Generated by jhipster:java:bootstrap generator 23 | [*.java] 24 | indent_size = 4 25 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | lint-staged 2 | -------------------------------------------------------------------------------- /.jhipster/BankAccount.json: -------------------------------------------------------------------------------- 1 | { 2 | "annotations": { 3 | "changelogDate": "20161016121722" 4 | }, 5 | "changelogDate": "20161016121722", 6 | "clientRootFolder": "jhipsterSampleMicroservice", 7 | "databaseType": "sql", 8 | "dto": "no", 9 | "entityTableName": "bank_account", 10 | "fields": [ 11 | { 12 | "fieldName": "name", 13 | "fieldType": "String", 14 | "fieldValidateRules": ["required"] 15 | }, 16 | { 17 | "fieldName": "balance", 18 | "fieldType": "BigDecimal", 19 | "fieldValidateRules": ["required"] 20 | } 21 | ], 22 | "fluentMethods": true, 23 | "microserviceName": "jhipsterSampleMicroservice", 24 | "name": "BankAccount", 25 | "pagination": "no", 26 | "relationships": [], 27 | "searchEngine": "no", 28 | "service": "no" 29 | } 30 | -------------------------------------------------------------------------------- /.lintstagedrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | '{,**/}*.{md,json,yml,js,cjs,mjs,ts,cts,mts,java,html,css,scss}': ['prettier --write'], 3 | }; 4 | -------------------------------------------------------------------------------- /.mvn/jvm.config: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-gateway/bad6f8ce2f5441dda8d5759e1ef1904de635972c/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | wrapperVersion=3.3.2 18 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip 19 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | .git 4 | 5 | # Generated by jhipster:maven 6 | target 7 | .mvn 8 | 9 | # Generated by jhipster:client 10 | target/classes/static/ 11 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | printWidth: 140 2 | singleQuote: true 3 | tabWidth: 2 4 | useTabs: false 5 | arrowParens: avoid 6 | bracketSameLine: false 7 | plugins: 8 | - prettier-plugin-packagejson 9 | - prettier-plugin-java 10 | overrides: 11 | - files: "*.java" 12 | options: 13 | tabWidth: 4 14 | -------------------------------------------------------------------------------- /checkstyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /cypress-audits.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'cypress'; 2 | import defaultConfig from './cypress.config'; 3 | 4 | export default defineConfig({ 5 | ...defaultConfig, 6 | e2e: { 7 | ...defaultConfig.e2e, 8 | specPattern: 'src/test/javascript/cypress/e2e/**/*.audits.ts', 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /cypress.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'cypress'; 2 | 3 | export default defineConfig({ 4 | video: false, 5 | fixturesFolder: 'src/test/javascript/cypress/fixtures', 6 | screenshotsFolder: 'target/cypress/screenshots', 7 | downloadsFolder: 'target/cypress/downloads', 8 | videosFolder: 'target/cypress/videos', 9 | chromeWebSecurity: true, 10 | viewportWidth: 1200, 11 | viewportHeight: 720, 12 | retries: 2, 13 | env: { 14 | authenticationUrl: '/api/authenticate', 15 | jwtStorageName: 'jhi-authenticationToken', 16 | }, 17 | e2e: { 18 | // We've imported your old cypress plugins here. 19 | // You may want to clean this up later by importing these. 20 | async setupNodeEvents(on, config) { 21 | // eslint-disable-next-line @typescript-eslint/no-unsafe-return 22 | return (await import('./src/test/javascript/cypress/plugins/index')).default(on, config); 23 | }, 24 | baseUrl: 'http://localhost:8080/', 25 | specPattern: 'src/test/javascript/cypress/e2e/**/*.cy.ts', 26 | supportFile: 'src/test/javascript/cypress/support/index.ts', 27 | experimentalRunAllSpecs: true, 28 | }, 29 | }); 30 | -------------------------------------------------------------------------------- /jest.conf.js: -------------------------------------------------------------------------------- 1 | const { pathsToModuleNameMapper } = require('ts-jest'); 2 | 3 | const { 4 | compilerOptions: { paths = {}, baseUrl = './' }, 5 | } = require('./tsconfig.json'); 6 | 7 | module.exports = { 8 | transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$|dayjs/esm)'], 9 | resolver: 'jest-preset-angular/build/resolvers/ng-jest-resolver.js', 10 | globals: { 11 | __VERSION__: 'test', 12 | }, 13 | roots: ['', `/${baseUrl}`], 14 | modulePaths: [`/${baseUrl}`], 15 | setupFiles: ['jest-date-mock'], 16 | cacheDirectory: '/target/jest-cache', 17 | coverageDirectory: '/target/test-results/', 18 | moduleNameMapper: pathsToModuleNameMapper(paths, { prefix: `/${baseUrl}/` }), 19 | reporters: [ 20 | 'default', 21 | ['jest-junit', { outputDirectory: '/target/test-results/', outputName: 'TESTS-results-jest.xml' }], 22 | ['jest-sonar', { outputDirectory: './target/test-results/jest', outputName: 'TESTS-results-sonar.xml' }], 23 | ], 24 | testMatch: ['/src/main/webapp/app/**/@(*.)@(spec.ts)'], 25 | testEnvironmentOptions: { 26 | url: 'https://jhipster.tech', 27 | }, 28 | }; 29 | -------------------------------------------------------------------------------- /ngsw-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/service-worker/config/schema.json", 3 | "index": "/index.html", 4 | "assetGroups": [ 5 | { 6 | "name": "app", 7 | "installMode": "prefetch", 8 | "resources": { 9 | "files": ["/favicon.ico", "/index.html", "/manifest.webapp", "/*.css", "/*.js"] 10 | } 11 | }, 12 | { 13 | "name": "assets", 14 | "installMode": "lazy", 15 | "updateMode": "prefetch", 16 | "resources": { 17 | "files": ["/content/**", "/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)"] 18 | } 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /npmw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | basedir=$(dirname "$0") 4 | 5 | if [ -f "$basedir/mvnw" ]; then 6 | bindir="$basedir/target/node" 7 | repodir="$basedir/target/node/node_modules" 8 | installCommand="$basedir/mvnw --batch-mode -ntp -Pwebapp frontend:install-node-and-npm@install-node-and-npm" 9 | else 10 | echo "Using npm installed globally" 11 | exec npm "$@" 12 | fi 13 | 14 | NPM_EXE="$repodir/npm/bin/npm-cli.js" 15 | NODE_EXE="$bindir/node" 16 | 17 | if [ ! -x "$NPM_EXE" ] || [ ! -x "$NODE_EXE" ]; then 18 | $installCommand || true 19 | fi 20 | 21 | if [ -x "$NODE_EXE" ]; then 22 | echo "Using node installed locally $($NODE_EXE --version)" 23 | PATH="$bindir:$PATH" 24 | else 25 | NODE_EXE='node' 26 | fi 27 | 28 | if [ ! -x "$NPM_EXE" ]; then 29 | echo "Local npm not found, using npm installed globally" 30 | npm "$@" 31 | else 32 | echo "Using npm installed locally $($NODE_EXE $NPM_EXE --version)" 33 | $NODE_EXE $NPM_EXE "$@" 34 | fi 35 | -------------------------------------------------------------------------------- /npmw.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | setlocal 4 | 5 | set NPMW_DIR=%~dp0 6 | 7 | set NODE_EXE=^"^" 8 | set NODE_PATH=%NPMW_DIR%target\node\ 9 | set NPM_EXE=^"%NPMW_DIR%target\node\npm.cmd^" 10 | set INSTALL_NPM_COMMAND=^"%NPMW_DIR%mvnw.cmd^" -Pwebapp frontend:install-node-and-npm@install-node-and-npm 11 | 12 | if not exist %NPM_EXE% ( 13 | call %INSTALL_NPM_COMMAND% 14 | ) 15 | 16 | if exist %NODE_EXE% ( 17 | Rem execute local npm with local node, whilst adding local node location to the PATH for this CMD session 18 | endlocal & echo "%PATH%"|find /i "%NODE_PATH%;">nul || set "PATH=%NODE_PATH%;%PATH%" & call %NODE_EXE% %NPM_EXE% %* 19 | ) else if exist %NPM_EXE% ( 20 | Rem execute local npm, whilst adding local npm location to the PATH for this CMD session 21 | endlocal & echo "%PATH%"|find /i "%NODE_PATH%;">nul || set "PATH=%NODE_PATH%;%PATH%" & call %NPM_EXE% %* 22 | ) else ( 23 | call npm %* 24 | ) 25 | -------------------------------------------------------------------------------- /src/main/docker/app.yml: -------------------------------------------------------------------------------- 1 | # This configuration is intended for development purpose, it's **your** responsibility to harden it for production 2 | name: jhipstersamplegateway 3 | services: 4 | app: 5 | image: jhipstersamplegateway 6 | environment: 7 | - _JAVA_OPTIONS=-Xmx512m -Xms256m 8 | - SPRING_PROFILES_ACTIVE=prod,api-docs 9 | - MANAGEMENT_PROMETHEUS_METRICS_EXPORT_ENABLED=true 10 | - SPRING_CLOUD_CONSUL_HOST=consul 11 | - SPRING_CLOUD_CONSUL_PORT=8500 12 | - SPRING_R2DBC_URL=r2dbc:mysql://mysql:3306/jhipstersamplegateway?useUnicode=true&characterEncoding=utf8&useSSL=false&useLegacyDatetimeCode=false&createDatabaseIfNotExist=true 13 | - SPRING_LIQUIBASE_URL=jdbc:mysql://mysql:3306/jhipstersamplegateway?useUnicode=true&characterEncoding=utf8&useSSL=false&useLegacyDatetimeCode=false&createDatabaseIfNotExist=true 14 | ports: 15 | - 127.0.0.1:8080:8080 16 | healthcheck: 17 | test: 18 | - CMD 19 | - curl 20 | - -f 21 | - http://localhost:8080/management/health 22 | interval: 5s 23 | timeout: 5s 24 | retries: 40 25 | depends_on: 26 | mysql: 27 | condition: service_healthy 28 | mysql: 29 | extends: 30 | file: ./mysql.yml 31 | service: mysql 32 | consul: 33 | extends: 34 | file: ./consul.yml 35 | service: consul 36 | consul-config-loader: 37 | extends: 38 | file: ./consul.yml 39 | service: consul-config-loader 40 | -------------------------------------------------------------------------------- /src/main/docker/central-server-config/README.md: -------------------------------------------------------------------------------- 1 | # Central configuration sources details 2 | 3 | When running the consul.yml or app.yml docker-compose files, files located in `central-server-config/` 4 | will get automatically loaded in Consul's K/V store. Adding or editing files will trigger a reloading. 5 | 6 | For more info, refer to https://www.jhipster.tech/consul/ 7 | -------------------------------------------------------------------------------- /src/main/docker/central-server-config/application.yml: -------------------------------------------------------------------------------- 1 | configserver: 2 | name: Docker Consul Service 3 | status: Connected to Consul Server running in Docker 4 | 5 | jhipster: 6 | security: 7 | authentication: 8 | jwt: 9 | # secret key which should be base64 encoded and changed in production 10 | base64-secret: bXktc2VjcmV0LWtleS13aGljaC1zaG91bGQtYmUtY2hhbmdlZC1pbi1wcm9kdWN0aW9uLWFuZC1iZS1iYXNlNjQtZW5jb2RlZAo= 11 | -------------------------------------------------------------------------------- /src/main/docker/config/git2consul.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0", 3 | "repos": [ 4 | { 5 | "name": "config", 6 | "url": "https://github.com/jhipster/generator-jhipster.git", 7 | "branches": ["main"], 8 | "include_branch_name": false, 9 | "source_root": "generators/server/src/main/docker/config/consul-config/", 10 | "hooks": [ 11 | { 12 | "type": "polling", 13 | "interval": "1" 14 | } 15 | ] 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /src/main/docker/consul.yml: -------------------------------------------------------------------------------- 1 | # This configuration is intended for development purpose, it's **your** responsibility to harden it for production 2 | name: jhipstersamplegateway 3 | services: 4 | consul: 5 | image: docker.io/bitnami/consul:1.21.0 6 | # If you want to expose these ports outside your dev PC, 7 | # remove the "127.0.0.1:" prefix 8 | ports: 9 | - 127.0.0.1:8300:8300 10 | - 127.0.0.1:8500:8500 11 | - 127.0.0.1:8600:8600 12 | command: consul agent -dev -ui -client 0.0.0.0 -log-level=INFO 13 | labels: 14 | org.springframework.boot.ignore: true 15 | 16 | consul-config-loader: 17 | image: jhipster/consul-config-loader:v0.4.1 18 | volumes: 19 | - ./central-server-config:/config 20 | environment: 21 | - INIT_SLEEP_SECONDS=5 22 | - CONSUL_URL=consul 23 | - CONSUL_PORT=8500 24 | # Uncomment to load configuration into Consul from a Git repository 25 | # as configured in central-server-config/git2consul.json 26 | # Also set SPRING_CLOUD_CONSUL_CONFIG_FORMAT=files on your apps 27 | # - CONFIG_MODE=git 28 | labels: 29 | org.springframework.boot.ignore: true 30 | -------------------------------------------------------------------------------- /src/main/docker/grafana/provisioning/dashboards/dashboard.yml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | providers: 4 | - name: 'Prometheus' 5 | orgId: 1 6 | folder: '' 7 | type: file 8 | disableDeletion: false 9 | editable: true 10 | options: 11 | path: /etc/grafana/provisioning/dashboards 12 | -------------------------------------------------------------------------------- /src/main/docker/jib/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "The application will start in ${JHIPSTER_SLEEP}s..." && sleep ${JHIPSTER_SLEEP} 4 | 5 | # usage: file_env VAR [DEFAULT] 6 | # ie: file_env 'XYZ_DB_PASSWORD' 'example' 7 | # (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of 8 | # "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature) 9 | file_env() { 10 | local var="$1" 11 | local fileVar="${var}_FILE" 12 | local def="${2:-}" 13 | if [[ ${!var:-} && ${!fileVar:-} ]]; then 14 | echo >&2 "error: both $var and $fileVar are set (but are exclusive)" 15 | exit 1 16 | fi 17 | local val="$def" 18 | if [[ ${!var:-} ]]; then 19 | val="${!var}" 20 | elif [[ ${!fileVar:-} ]]; then 21 | val="$(< "${!fileVar}")" 22 | fi 23 | 24 | if [[ -n $val ]]; then 25 | export "$var"="$val" 26 | fi 27 | 28 | unset "$fileVar" 29 | } 30 | 31 | file_env 'SPRING_DATASOURCE_URL' 32 | file_env 'SPRING_DATASOURCE_USERNAME' 33 | file_env 'SPRING_DATASOURCE_PASSWORD' 34 | file_env 'SPRING_LIQUIBASE_URL' 35 | file_env 'SPRING_LIQUIBASE_USER' 36 | file_env 'SPRING_LIQUIBASE_PASSWORD' 37 | file_env 'JHIPSTER_REGISTRY_PASSWORD' 38 | 39 | exec java ${JAVA_OPTS} -noverify -XX:+AlwaysPreTouch -Djava.security.egd=file:/dev/./urandom -cp /app/resources/:/app/classes/:/app/libs/* "io.github.jhipster.sample.JhipsterSampleGatewayApp" "$@" 40 | -------------------------------------------------------------------------------- /src/main/docker/monitoring.yml: -------------------------------------------------------------------------------- 1 | # This configuration is intended for development purpose, it's **your** responsibility to harden it for production 2 | name: jhipstersamplegateway 3 | services: 4 | prometheus: 5 | image: prom/prometheus:v3.3.1 6 | volumes: 7 | - ./prometheus/:/etc/prometheus/ 8 | command: 9 | - '--config.file=/etc/prometheus/prometheus.yml' 10 | # If you want to expose these ports outside your dev PC, 11 | # remove the "127.0.0.1:" prefix 12 | ports: 13 | - 127.0.0.1:9090:9090 14 | # On MacOS, remove next line and replace localhost by host.docker.internal in prometheus/prometheus.yml and 15 | # grafana/provisioning/datasources/datasource.yml 16 | network_mode: 'host' # to test locally running service 17 | grafana: 18 | image: grafana/grafana:12.0.0 19 | volumes: 20 | - ./grafana/provisioning/:/etc/grafana/provisioning/ 21 | environment: 22 | - GF_SECURITY_ADMIN_PASSWORD=admin 23 | - GF_USERS_ALLOW_SIGN_UP=false 24 | - GF_INSTALL_PLUGINS=grafana-piechart-panel 25 | # If you want to expose these ports outside your dev PC, 26 | # remove the "127.0.0.1:" prefix 27 | ports: 28 | - 127.0.0.1:3000:3000 29 | # On MacOS, remove next line and replace localhost by host.docker.internal in prometheus/prometheus.yml and 30 | # grafana/provisioning/datasources/datasource.yml 31 | network_mode: 'host' # to test locally running service 32 | -------------------------------------------------------------------------------- /src/main/docker/mysql.yml: -------------------------------------------------------------------------------- 1 | # This configuration is intended for development purpose, it's **your** responsibility to harden it for production 2 | name: jhipstersamplegateway 3 | services: 4 | mysql: 5 | image: mysql:9.2.0 6 | volumes: 7 | - ./config/mysql:/etc/mysql/conf.d 8 | environment: 9 | - MYSQL_ALLOW_EMPTY_PASSWORD=yes 10 | - MYSQL_DATABASE=jhipstersamplegateway 11 | # If you want to expose these ports outside your dev PC, 12 | # remove the "127.0.0.1:" prefix 13 | ports: 14 | - 127.0.0.1:3306:3306 15 | command: mysqld --lower_case_table_names=1 --skip-mysqlx --character_set_server=utf8mb4 --explicit_defaults_for_timestamp 16 | healthcheck: 17 | test: ['CMD-SHELL', 'mysql -e "SHOW DATABASES;" && sleep 5'] 18 | interval: 5s 19 | timeout: 10s 20 | retries: 10 21 | -------------------------------------------------------------------------------- /src/main/docker/prometheus/prometheus.yml: -------------------------------------------------------------------------------- 1 | # Sample global config for monitoring JHipster applications 2 | global: 3 | scrape_interval: 15s # By default, scrape targets every 15 seconds. 4 | evaluation_interval: 15s # By default, scrape targets every 15 seconds. 5 | # scrape_timeout is set to the global default (10s). 6 | 7 | # Attach these labels to any time series or alerts when communicating with 8 | # external systems (federation, remote storage, Alertmanager). 9 | external_labels: 10 | monitor: 'jhipster' 11 | 12 | # A scrape configuration containing exactly one endpoint to scrape: 13 | # Here it's Prometheus itself. 14 | scrape_configs: 15 | # The job name is added as a label `job=` to any timeseries scraped from this config. 16 | - job_name: 'prometheus' 17 | 18 | # Override the global default and scrape targets from this job every 5 seconds. 19 | scrape_interval: 5s 20 | 21 | # scheme defaults to 'http' enable https in case your application is server via https 22 | #scheme: https 23 | # basic auth is not needed by default. See https://www.jhipster.tech/monitoring/#configuring-metrics-forwarding for details 24 | #basic_auth: 25 | # username: admin 26 | # password: admin 27 | metrics_path: /management/prometheus 28 | static_configs: 29 | - targets: 30 | # On MacOS, replace localhost by host.docker.internal 31 | - localhost:8080 32 | -------------------------------------------------------------------------------- /src/main/docker/services.yml: -------------------------------------------------------------------------------- 1 | # This configuration is intended for development purpose, it's **your** responsibility to harden it for production 2 | name: jhipstersamplegateway 3 | services: 4 | mysql: 5 | extends: 6 | file: ./mysql.yml 7 | service: mysql 8 | profiles: 9 | - '' 10 | - prod 11 | consul: 12 | extends: 13 | file: ./consul.yml 14 | service: consul 15 | consul-config-loader: 16 | extends: 17 | file: ./consul.yml 18 | service: consul-config-loader 19 | -------------------------------------------------------------------------------- /src/main/docker/sonar.yml: -------------------------------------------------------------------------------- 1 | # This configuration is intended for development purpose, it's **your** responsibility to harden it for production 2 | name: jhipstersamplegateway 3 | services: 4 | sonar: 5 | container_name: sonarqube 6 | image: sonarqube:25.5.0.107428-community 7 | # Forced authentication redirect for UI is turned off for out of the box experience while trying out SonarQube 8 | # For real use cases delete SONAR_FORCEAUTHENTICATION variable or set SONAR_FORCEAUTHENTICATION=true 9 | environment: 10 | - SONAR_FORCEAUTHENTICATION=false 11 | # If you want to expose these ports outside your dev PC, 12 | # remove the "127.0.0.1:" prefix 13 | ports: 14 | - 127.0.0.1:9001:9000 15 | - 127.0.0.1:9000:9000 16 | -------------------------------------------------------------------------------- /src/main/docker/zipkin.yml: -------------------------------------------------------------------------------- 1 | # This configuration is intended for development purpose, it's **your** responsibility to harden it for production 2 | name: jhipstersamplegateway 3 | services: 4 | zipkin: 5 | image: openzipkin/zipkin:3.5.1 6 | ports: 7 | - 127.0.0.1:9411:9411 8 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/GeneratedByJHipster.java: -------------------------------------------------------------------------------- 1 | package io.github.jhipster.sample; 2 | 3 | import jakarta.annotation.Generated; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | @Generated(value = "JHipster", comments = "Generated by JHipster 8.11.0") 10 | @Retention(RetentionPolicy.SOURCE) 11 | @Target({ ElementType.TYPE }) 12 | public @interface GeneratedByJHipster { 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/aop/logging/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Logging aspect. 3 | */ 4 | package io.github.jhipster.sample.aop.logging; 5 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/config/ApplicationProperties.java: -------------------------------------------------------------------------------- 1 | package io.github.jhipster.sample.config; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | 5 | /** 6 | * Properties specific to Jhipster Sample Gateway. 7 | *

8 | * Properties are configured in the {@code application.yml} file. 9 | * See {@link tech.jhipster.config.JHipsterProperties} for a good example. 10 | */ 11 | @ConfigurationProperties(prefix = "application", ignoreUnknownFields = false) 12 | public class ApplicationProperties { 13 | // jhipster-needle-application-properties-property 14 | 15 | // jhipster-needle-application-properties-property-getter 16 | 17 | // jhipster-needle-application-properties-property-class 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/config/Constants.java: -------------------------------------------------------------------------------- 1 | package io.github.jhipster.sample.config; 2 | 3 | /** 4 | * Application constants. 5 | */ 6 | public final class Constants { 7 | 8 | // Regex for acceptable logins 9 | public static final String LOGIN_REGEX = "^(?>[a-zA-Z0-9!$&*+=?^_`{|}~.-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*)|(?>[_.@A-Za-z0-9-]+)$"; 10 | 11 | public static final String SYSTEM = "system"; 12 | public static final String DEFAULT_LANGUAGE = "en"; 13 | 14 | private Constants() {} 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/config/DateTimeFormatConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.github.jhipster.sample.config; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.format.FormatterRegistry; 5 | import org.springframework.format.datetime.standard.DateTimeFormatterRegistrar; 6 | import org.springframework.web.reactive.config.WebFluxConfigurer; 7 | 8 | /** 9 | * Configure the converters to use the ISO format for dates by default. 10 | */ 11 | @Configuration 12 | public class DateTimeFormatConfiguration implements WebFluxConfigurer { 13 | 14 | @Override 15 | public void addFormatters(FormatterRegistry registry) { 16 | DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar(); 17 | registrar.setUseIsoFormat(true); 18 | registrar.registerFormatters(registry); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/config/JacksonConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.github.jhipster.sample.config; 2 | 3 | import com.fasterxml.jackson.core.JsonGenerator; 4 | import com.fasterxml.jackson.databind.JsonSerializer; 5 | import com.fasterxml.jackson.databind.SerializerProvider; 6 | import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; 7 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 8 | import java.io.IOException; 9 | import java.time.LocalTime; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.context.annotation.Configuration; 12 | 13 | @Configuration 14 | public class JacksonConfiguration { 15 | 16 | /** 17 | * Support for Java date and time API. 18 | * @return the corresponding Jackson module. 19 | */ 20 | @Bean 21 | public JavaTimeModule javaTimeModule() { 22 | final JavaTimeModule javaTime = new JavaTimeModule(); 23 | javaTime.addSerializer( 24 | LocalTime.class, 25 | new JsonSerializer() { 26 | @Override 27 | public void serialize(LocalTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException { 28 | gen.writeString(value.toString()); 29 | } 30 | } 31 | ); 32 | return javaTime; 33 | } 34 | 35 | @Bean 36 | public Jdk8Module jdk8TimeModule() { 37 | return new Jdk8Module(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/config/LoggingAspectConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.github.jhipster.sample.config; 2 | 3 | import io.github.jhipster.sample.aop.logging.LoggingAspect; 4 | import org.springframework.context.annotation.*; 5 | import org.springframework.core.env.Environment; 6 | import tech.jhipster.config.JHipsterConstants; 7 | 8 | @Configuration 9 | @EnableAspectJAutoProxy 10 | public class LoggingAspectConfiguration { 11 | 12 | @Bean 13 | @Profile(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) 14 | public LoggingAspect loggingAspect(Environment env) { 15 | return new LoggingAspect(env); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/config/ReactorConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.github.jhipster.sample.config; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.context.annotation.Profile; 5 | import reactor.core.publisher.Hooks; 6 | import tech.jhipster.config.JHipsterConstants; 7 | 8 | @Configuration 9 | @Profile("!" + JHipsterConstants.SPRING_PROFILE_PRODUCTION) 10 | public class ReactorConfiguration { 11 | 12 | public ReactorConfiguration() { 13 | Hooks.onOperatorDebug(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/config/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Application configuration. 3 | */ 4 | package io.github.jhipster.sample.config; 5 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/domain/AuthorityCallback.java: -------------------------------------------------------------------------------- 1 | package io.github.jhipster.sample.domain; 2 | 3 | import org.reactivestreams.Publisher; 4 | import org.springframework.data.r2dbc.mapping.OutboundRow; 5 | import org.springframework.data.r2dbc.mapping.event.AfterConvertCallback; 6 | import org.springframework.data.r2dbc.mapping.event.AfterSaveCallback; 7 | import org.springframework.data.relational.core.sql.SqlIdentifier; 8 | import org.springframework.stereotype.Component; 9 | import reactor.core.publisher.Mono; 10 | 11 | @Component 12 | public class AuthorityCallback implements AfterSaveCallback, AfterConvertCallback { 13 | 14 | @Override 15 | public Publisher onAfterConvert(Authority entity, SqlIdentifier table) { 16 | return Mono.just(entity.setIsPersisted()); 17 | } 18 | 19 | @Override 20 | public Publisher onAfterSave(Authority entity, OutboundRow outboundRow, SqlIdentifier table) { 21 | return Mono.just(entity.setIsPersisted()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/domain/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Domain objects. 3 | */ 4 | package io.github.jhipster.sample.domain; 5 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/management/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Application management. 3 | */ 4 | package io.github.jhipster.sample.management; 5 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Application root. 3 | */ 4 | package io.github.jhipster.sample; 5 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/repository/AuthorityRepository.java: -------------------------------------------------------------------------------- 1 | package io.github.jhipster.sample.repository; 2 | 3 | import io.github.jhipster.sample.domain.Authority; 4 | import org.springframework.data.r2dbc.repository.R2dbcRepository; 5 | import org.springframework.stereotype.Repository; 6 | 7 | /** 8 | * Spring Data R2DBC repository for the Authority entity. 9 | */ 10 | @SuppressWarnings("unused") 11 | @Repository 12 | public interface AuthorityRepository extends R2dbcRepository {} 13 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/repository/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Repository layer. 3 | */ 4 | package io.github.jhipster.sample.repository; 5 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/repository/rowmapper/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Webflux database column mapper. 3 | */ 4 | package io.github.jhipster.sample.repository.rowmapper; 5 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/security/AuthoritiesConstants.java: -------------------------------------------------------------------------------- 1 | package io.github.jhipster.sample.security; 2 | 3 | /** 4 | * Constants for Spring Security authorities. 5 | */ 6 | public final class AuthoritiesConstants { 7 | 8 | public static final String ADMIN = "ROLE_ADMIN"; 9 | 10 | public static final String USER = "ROLE_USER"; 11 | 12 | public static final String ANONYMOUS = "ROLE_ANONYMOUS"; 13 | 14 | private AuthoritiesConstants() {} 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/security/UserNotActivatedException.java: -------------------------------------------------------------------------------- 1 | package io.github.jhipster.sample.security; 2 | 3 | import org.springframework.security.core.AuthenticationException; 4 | 5 | /** 6 | * This exception is thrown in case of a not activated user trying to authenticate. 7 | */ 8 | public class UserNotActivatedException extends AuthenticationException { 9 | 10 | private static final long serialVersionUID = 1L; 11 | 12 | public UserNotActivatedException(String message) { 13 | super(message); 14 | } 15 | 16 | public UserNotActivatedException(String message, Throwable t) { 17 | super(message, t); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/security/jwt/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * This package file was generated by JHipster 3 | */ 4 | package io.github.jhipster.sample.security.jwt; 5 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/security/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Application security utilities. 3 | */ 4 | package io.github.jhipster.sample.security; 5 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/service/EmailAlreadyUsedException.java: -------------------------------------------------------------------------------- 1 | package io.github.jhipster.sample.service; 2 | 3 | public class EmailAlreadyUsedException extends RuntimeException { 4 | 5 | private static final long serialVersionUID = 1L; 6 | 7 | public EmailAlreadyUsedException() { 8 | super("Email is already in use!"); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/service/InvalidPasswordException.java: -------------------------------------------------------------------------------- 1 | package io.github.jhipster.sample.service; 2 | 3 | public class InvalidPasswordException extends RuntimeException { 4 | 5 | private static final long serialVersionUID = 1L; 6 | 7 | public InvalidPasswordException() { 8 | super("Incorrect password"); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/service/UsernameAlreadyUsedException.java: -------------------------------------------------------------------------------- 1 | package io.github.jhipster.sample.service; 2 | 3 | public class UsernameAlreadyUsedException extends RuntimeException { 4 | 5 | private static final long serialVersionUID = 1L; 6 | 7 | public UsernameAlreadyUsedException() { 8 | super("Login name already used!"); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/service/dto/PasswordChangeDTO.java: -------------------------------------------------------------------------------- 1 | package io.github.jhipster.sample.service.dto; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * A DTO representing a password change required data - current and new password. 7 | */ 8 | public class PasswordChangeDTO implements Serializable { 9 | 10 | private static final long serialVersionUID = 1L; 11 | 12 | private String currentPassword; 13 | private String newPassword; 14 | 15 | public PasswordChangeDTO() { 16 | // Empty constructor needed for Jackson. 17 | } 18 | 19 | public PasswordChangeDTO(String currentPassword, String newPassword) { 20 | this.currentPassword = currentPassword; 21 | this.newPassword = newPassword; 22 | } 23 | 24 | public String getCurrentPassword() { 25 | return currentPassword; 26 | } 27 | 28 | public void setCurrentPassword(String currentPassword) { 29 | this.currentPassword = currentPassword; 30 | } 31 | 32 | public String getNewPassword() { 33 | return newPassword; 34 | } 35 | 36 | public void setNewPassword(String newPassword) { 37 | this.newPassword = newPassword; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/service/dto/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Data transfer objects for rest mapping. 3 | */ 4 | package io.github.jhipster.sample.service.dto; 5 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/service/mapper/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Data transfer objects mappers. 3 | */ 4 | package io.github.jhipster.sample.service.mapper; 5 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/service/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Service layer. 3 | */ 4 | package io.github.jhipster.sample.service; 5 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/web/filter/SpaWebFilter.java: -------------------------------------------------------------------------------- 1 | package io.github.jhipster.sample.web.filter; 2 | 3 | import org.springframework.web.server.ServerWebExchange; 4 | import org.springframework.web.server.WebFilter; 5 | import org.springframework.web.server.WebFilterChain; 6 | import reactor.core.publisher.Mono; 7 | 8 | public class SpaWebFilter implements WebFilter { 9 | 10 | /** 11 | * Forwards any unmapped paths (except those containing a period) to the client {@code index.html}. 12 | */ 13 | @Override 14 | public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { 15 | String path = exchange.getRequest().getURI().getPath(); 16 | if ( 17 | !path.startsWith("/api") && 18 | !path.startsWith("/management") && 19 | !path.startsWith("/v3/api-docs") && 20 | !path.startsWith("/services") && 21 | !path.contains(".") && 22 | path.matches("/(.*)") 23 | ) { 24 | return chain.filter(exchange.mutate().request(exchange.getRequest().mutate().path("/index.html").build()).build()); 25 | } 26 | return chain.filter(exchange); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/web/filter/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Request chain filters. 3 | */ 4 | package io.github.jhipster.sample.web.filter; 5 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/web/rest/errors/EmailAlreadyUsedException.java: -------------------------------------------------------------------------------- 1 | package io.github.jhipster.sample.web.rest.errors; 2 | 3 | @SuppressWarnings("java:S110") // Inheritance tree of classes should not be too deep 4 | public class EmailAlreadyUsedException extends BadRequestAlertException { 5 | 6 | private static final long serialVersionUID = 1L; 7 | 8 | public EmailAlreadyUsedException() { 9 | super(ErrorConstants.EMAIL_ALREADY_USED_TYPE, "Email is already in use!", "userManagement", "emailexists"); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/web/rest/errors/ErrorConstants.java: -------------------------------------------------------------------------------- 1 | package io.github.jhipster.sample.web.rest.errors; 2 | 3 | import java.net.URI; 4 | 5 | public final class ErrorConstants { 6 | 7 | public static final String ERR_CONCURRENCY_FAILURE = "error.concurrencyFailure"; 8 | public static final String ERR_VALIDATION = "error.validation"; 9 | public static final String PROBLEM_BASE_URL = "https://www.jhipster.tech/problem"; 10 | public static final URI DEFAULT_TYPE = URI.create(PROBLEM_BASE_URL + "/problem-with-message"); 11 | public static final URI CONSTRAINT_VIOLATION_TYPE = URI.create(PROBLEM_BASE_URL + "/constraint-violation"); 12 | public static final URI INVALID_PASSWORD_TYPE = URI.create(PROBLEM_BASE_URL + "/invalid-password"); 13 | public static final URI EMAIL_ALREADY_USED_TYPE = URI.create(PROBLEM_BASE_URL + "/email-already-used"); 14 | public static final URI LOGIN_ALREADY_USED_TYPE = URI.create(PROBLEM_BASE_URL + "/login-already-used"); 15 | 16 | private ErrorConstants() {} 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/web/rest/errors/FieldErrorVM.java: -------------------------------------------------------------------------------- 1 | package io.github.jhipster.sample.web.rest.errors; 2 | 3 | import java.io.Serializable; 4 | 5 | public class FieldErrorVM implements Serializable { 6 | 7 | private static final long serialVersionUID = 1L; 8 | 9 | private final String objectName; 10 | 11 | private final String field; 12 | 13 | private final String message; 14 | 15 | public FieldErrorVM(String dto, String field, String message) { 16 | this.objectName = dto; 17 | this.field = field; 18 | this.message = message; 19 | } 20 | 21 | public String getObjectName() { 22 | return objectName; 23 | } 24 | 25 | public String getField() { 26 | return field; 27 | } 28 | 29 | public String getMessage() { 30 | return message; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/web/rest/errors/InvalidPasswordException.java: -------------------------------------------------------------------------------- 1 | package io.github.jhipster.sample.web.rest.errors; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.web.ErrorResponseException; 5 | import tech.jhipster.web.rest.errors.ProblemDetailWithCause.ProblemDetailWithCauseBuilder; 6 | 7 | @SuppressWarnings("java:S110") // Inheritance tree of classes should not be too deep 8 | public class InvalidPasswordException extends ErrorResponseException { 9 | 10 | private static final long serialVersionUID = 1L; 11 | 12 | public InvalidPasswordException() { 13 | super( 14 | HttpStatus.BAD_REQUEST, 15 | ProblemDetailWithCauseBuilder.instance() 16 | .withStatus(HttpStatus.BAD_REQUEST.value()) 17 | .withType(ErrorConstants.INVALID_PASSWORD_TYPE) 18 | .withTitle("Incorrect password") 19 | .build(), 20 | null 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/web/rest/errors/LoginAlreadyUsedException.java: -------------------------------------------------------------------------------- 1 | package io.github.jhipster.sample.web.rest.errors; 2 | 3 | @SuppressWarnings("java:S110") // Inheritance tree of classes should not be too deep 4 | public class LoginAlreadyUsedException extends BadRequestAlertException { 5 | 6 | private static final long serialVersionUID = 1L; 7 | 8 | public LoginAlreadyUsedException() { 9 | super(ErrorConstants.LOGIN_ALREADY_USED_TYPE, "Login name already used!", "userManagement", "userexists"); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/web/rest/errors/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Rest layer error handling. 3 | */ 4 | package io.github.jhipster.sample.web.rest.errors; 5 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/web/rest/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Rest layer. 3 | */ 4 | package io.github.jhipster.sample.web.rest; 5 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/web/rest/vm/KeyAndPasswordVM.java: -------------------------------------------------------------------------------- 1 | package io.github.jhipster.sample.web.rest.vm; 2 | 3 | /** 4 | * View Model object for storing the user's key and password. 5 | */ 6 | public class KeyAndPasswordVM { 7 | 8 | private String key; 9 | 10 | private String newPassword; 11 | 12 | public String getKey() { 13 | return key; 14 | } 15 | 16 | public void setKey(String key) { 17 | this.key = key; 18 | } 19 | 20 | public String getNewPassword() { 21 | return newPassword; 22 | } 23 | 24 | public void setNewPassword(String newPassword) { 25 | this.newPassword = newPassword; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/web/rest/vm/LoginVM.java: -------------------------------------------------------------------------------- 1 | package io.github.jhipster.sample.web.rest.vm; 2 | 3 | import jakarta.validation.constraints.NotNull; 4 | import jakarta.validation.constraints.Size; 5 | 6 | /** 7 | * View Model object for storing a user's credentials. 8 | */ 9 | public class LoginVM { 10 | 11 | @NotNull 12 | @Size(min = 1, max = 50) 13 | private String username; 14 | 15 | @NotNull 16 | @Size(min = 4, max = 100) 17 | private String password; 18 | 19 | private boolean rememberMe; 20 | 21 | public String getUsername() { 22 | return username; 23 | } 24 | 25 | public void setUsername(String username) { 26 | this.username = username; 27 | } 28 | 29 | public String getPassword() { 30 | return password; 31 | } 32 | 33 | public void setPassword(String password) { 34 | this.password = password; 35 | } 36 | 37 | public boolean isRememberMe() { 38 | return rememberMe; 39 | } 40 | 41 | public void setRememberMe(boolean rememberMe) { 42 | this.rememberMe = rememberMe; 43 | } 44 | 45 | // prettier-ignore 46 | @Override 47 | public String toString() { 48 | return "LoginVM{" + 49 | "username='" + username + '\'' + 50 | ", rememberMe=" + rememberMe + 51 | '}'; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/web/rest/vm/ManagedUserVM.java: -------------------------------------------------------------------------------- 1 | package io.github.jhipster.sample.web.rest.vm; 2 | 3 | import io.github.jhipster.sample.service.dto.AdminUserDTO; 4 | import jakarta.validation.constraints.Size; 5 | 6 | /** 7 | * View Model extending the AdminUserDTO, which is meant to be used in the user management UI. 8 | */ 9 | public class ManagedUserVM extends AdminUserDTO { 10 | 11 | public static final int PASSWORD_MIN_LENGTH = 4; 12 | 13 | public static final int PASSWORD_MAX_LENGTH = 100; 14 | 15 | @Size(min = PASSWORD_MIN_LENGTH, max = PASSWORD_MAX_LENGTH) 16 | private String password; 17 | 18 | public ManagedUserVM() { 19 | // Empty constructor needed for Jackson. 20 | } 21 | 22 | public String getPassword() { 23 | return password; 24 | } 25 | 26 | public void setPassword(String password) { 27 | this.password = password; 28 | } 29 | 30 | // prettier-ignore 31 | @Override 32 | public String toString() { 33 | return "ManagedUserVM{" + super.toString() + "} "; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/web/rest/vm/RouteVM.java: -------------------------------------------------------------------------------- 1 | package io.github.jhipster.sample.web.rest.vm; 2 | 3 | import java.util.List; 4 | import org.springframework.cloud.client.ServiceInstance; 5 | 6 | /** 7 | * View Model that stores a route managed by the Gateway. 8 | */ 9 | public class RouteVM { 10 | 11 | private String path; 12 | 13 | private String serviceId; 14 | 15 | private List serviceInstances; 16 | 17 | public String getPath() { 18 | return path; 19 | } 20 | 21 | public void setPath(String path) { 22 | this.path = path; 23 | } 24 | 25 | public String getServiceId() { 26 | return serviceId; 27 | } 28 | 29 | public void setServiceId(String serviceId) { 30 | this.serviceId = serviceId; 31 | } 32 | 33 | public List getServiceInstances() { 34 | return serviceInstances; 35 | } 36 | 37 | public void setServiceInstances(List serviceInstances) { 38 | this.serviceInstances = serviceInstances; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/io/github/jhipster/sample/web/rest/vm/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Rest layer visual models. 3 | */ 4 | package io.github.jhipster.sample.web.rest.vm; 5 | -------------------------------------------------------------------------------- /src/main/resources/.h2.server.properties: -------------------------------------------------------------------------------- 1 | #H2 Server Properties 2 | 0=JHipster H2 (Disk)|org.h2.Driver|jdbc\:h2\:file\:./target/h2db/db/jhipstersamplegateway|jhipsterSampleGateway 3 | webAllowOthers=true 4 | webPort=8092 5 | webSSL=false 6 | -------------------------------------------------------------------------------- /src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | 2 | ${AnsiColor.GREEN} ██╗${AnsiColor.RED} ██╗ ██╗ ████████╗ ███████╗ ██████╗ ████████╗ ████████╗ ███████╗ 3 | ${AnsiColor.GREEN} ██║${AnsiColor.RED} ██║ ██║ ╚══██╔══╝ ██╔═══██╗ ██╔════╝ ╚══██╔══╝ ██╔═════╝ ██╔═══██╗ 4 | ${AnsiColor.GREEN} ██║${AnsiColor.RED} ████████║ ██║ ███████╔╝ ╚█████╗ ██║ ██████╗ ███████╔╝ 5 | ${AnsiColor.GREEN}██╗ ██║${AnsiColor.RED} ██╔═══██║ ██║ ██╔════╝ ╚═══██╗ ██║ ██╔═══╝ ██╔══██║ 6 | ${AnsiColor.GREEN}╚██████╔╝${AnsiColor.RED} ██║ ██║ ████████╗ ██║ ██████╔╝ ██║ ████████╗ ██║ ╚██╗ 7 | ${AnsiColor.GREEN} ╚═════╝ ${AnsiColor.RED} ╚═╝ ╚═╝ ╚═══════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══════╝ ╚═╝ ╚═╝ 8 | 9 | ${AnsiColor.BRIGHT_BLUE}:: JHipster 🤓 :: Running Spring Boot ${spring-boot.version} :: Startup profile(s) ${spring.profiles.active} :: 10 | :: https://www.jhipster.tech ::${AnsiColor.DEFAULT} 11 | -------------------------------------------------------------------------------- /src/main/resources/config/application-tls.yml: -------------------------------------------------------------------------------- 1 | # =================================================================== 2 | # Activate this profile to enable TLS and HTTP/2. 3 | # 4 | # JHipster has generated a self-signed certificate, which will be used to encrypt traffic. 5 | # As your browser will not understand this certificate, you will need to import it. 6 | # 7 | # Another (easiest) solution with Chrome is to enable the "allow-insecure-localhost" flag 8 | # at chrome://flags/#allow-insecure-localhost 9 | # =================================================================== 10 | server: 11 | ssl: 12 | key-store: classpath:config/tls/keystore.p12 13 | key-store-password: password 14 | key-store-type: PKCS12 15 | key-alias: selfsigned 16 | ciphers: 17 | - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 18 | - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 19 | - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA 20 | - TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA 21 | - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 22 | - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 23 | - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA 24 | - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA 25 | enabled-protocols: TLSv1.2 26 | http2: 27 | enabled: true 28 | -------------------------------------------------------------------------------- /src/main/resources/config/bootstrap-prod.yml: -------------------------------------------------------------------------------- 1 | # =================================================================== 2 | # Spring Cloud Consul Config bootstrap configuration for the "prod" profile 3 | # =================================================================== 4 | 5 | spring: 6 | cloud: 7 | consul: 8 | config: 9 | fail-fast: true 10 | format: yaml # set this to "files" if using git2consul 11 | profile-separator: '-' 12 | retry: 13 | initial-interval: 1000 14 | max-interval: 2000 15 | max-attempts: 100 16 | -------------------------------------------------------------------------------- /src/main/resources/config/bootstrap.yml: -------------------------------------------------------------------------------- 1 | # =================================================================== 2 | # Spring Cloud Consul Config bootstrap configuration for the "dev" profile 3 | # In prod profile, properties will be overwritten by the ones defined in bootstrap-prod.yml 4 | # =================================================================== 5 | 6 | spring: 7 | application: 8 | name: jhipsterSampleGateway 9 | profiles: 10 | # The commented value for `active` can be replaced with valid Spring profiles to load. 11 | # Otherwise, it will be filled in by maven when building the JAR file 12 | # Either way, it can be overridden by `--spring.profiles.active` value passed in the commandline or `-Dspring.profiles.active` set in `JAVA_OPTS` 13 | active: '@spring.profiles.active@' 14 | cloud: 15 | consul: 16 | config: 17 | fail-fast: false # if not in "prod" profile, do not force to use Spring Cloud Config 18 | format: yaml 19 | profile-separator: '-' 20 | discovery: 21 | tags: 22 | - profile=${spring.profiles.active} 23 | - version='@project.version@' 24 | - git-version=${git.commit.id.describe:} 25 | - git-commit=${git.commit.id.abbrev:} 26 | - git-branch=${git.branch:} 27 | - context-path=${server.servlet.context-path:} 28 | 29 | host: localhost 30 | port: 8500 31 | docker: 32 | compose: 33 | enabled: true 34 | lifecycle-management: start-only 35 | file: src/main/docker/services.yml 36 | -------------------------------------------------------------------------------- /src/main/resources/config/liquibase/data/authority.csv: -------------------------------------------------------------------------------- 1 | name 2 | ROLE_ADMIN 3 | ROLE_USER 4 | -------------------------------------------------------------------------------- /src/main/resources/config/liquibase/data/user.csv: -------------------------------------------------------------------------------- 1 | id;login;password_hash;first_name;last_name;email;image_url;activated;lang_key;created_by;last_modified_by 2 | 1;admin;$2a$10$gSAhZrxMllrbgj/kkK9UceBPpChGWJA7SYIb1Mqo.n5aNLq1/oRrC;Administrator;Administrator;admin@localhost;;true;en;system;system 3 | 2;user;$2a$10$VEjxo0jq2YG9Rbk2HmX9S.k1uZBGYUHdUcid3g/vfiEl7lwWgOH/K;User;User;user@localhost;;true;en;system;system 4 | -------------------------------------------------------------------------------- /src/main/resources/config/liquibase/data/user_authority.csv: -------------------------------------------------------------------------------- 1 | user_id;authority_name 2 | 1;ROLE_ADMIN 3 | 1;ROLE_USER 4 | 2;ROLE_USER 5 | -------------------------------------------------------------------------------- /src/main/resources/config/tls/keystore.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-gateway/bad6f8ce2f5441dda8d5759e1ef1904de635972c/src/main/resources/config/tls/keystore.p12 -------------------------------------------------------------------------------- /src/main/resources/i18n/messages.properties: -------------------------------------------------------------------------------- 1 | # Error page 2 | error.title=Your request cannot be processed 3 | error.subtitle=Sorry, an error has occurred. 4 | error.status=Status: 5 | error.message=Message: 6 | 7 | # Activation email 8 | email.activation.title=jhipsterSampleGateway account activation is required 9 | email.activation.greeting=Dear {0} 10 | email.activation.text1=Your jhipsterSampleGateway account has been created, please click on the URL below to activate it: 11 | email.activation.text2=Regards, 12 | email.signature=jhipsterSampleGateway Team. 13 | 14 | # Creation email 15 | email.creation.text1=Your jhipsterSampleGateway account has been created, please click on the URL below to access it: 16 | 17 | # Reset email 18 | email.reset.title=jhipsterSampleGateway password reset 19 | email.reset.greeting=Dear {0} 20 | email.reset.text1=For your jhipsterSampleGateway account a password reset was requested, please click on the URL below to reset it: 21 | email.reset.text2=Regards, 22 | -------------------------------------------------------------------------------- /src/main/resources/templates/mail/activationEmail.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | JHipster activation 5 | 6 | 7 | 8 | 9 |

Dear

10 |

Your JHipster account has been created, please click on the URL below to activate it:

11 |

12 | Activation link 13 |

14 |

15 | Regards, 16 |
17 | JHipster. 18 |

19 | 20 | 21 | -------------------------------------------------------------------------------- /src/main/resources/templates/mail/creationEmail.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | JHipster creation 5 | 6 | 7 | 8 | 9 |

Dear

10 |

Your JHipster account has been created, please click on the URL below to access it:

11 |

12 | Login link 13 |

14 |

15 | Regards, 16 |
17 | JHipster. 18 |

19 | 20 | 21 | -------------------------------------------------------------------------------- /src/main/resources/templates/mail/passwordResetEmail.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | JHipster password reset 5 | 6 | 7 | 8 | 9 |

Dear

10 |

11 | For your JHipster account a password reset was requested, please click on the URL below to reset it: 12 |

13 |

14 | Login link 15 |

16 |

17 | Regards, 18 |
19 | JHipster. 20 |

21 | 22 | 23 | -------------------------------------------------------------------------------- /src/main/webapp/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Page Not Found 6 | 7 | 8 | 52 | 53 | 54 |

Page Not Found

55 |

Sorry, but the page you were trying to view does not exist.

56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | html 10 | text/html;charset=utf-8 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/main/webapp/app/account/account.route.ts: -------------------------------------------------------------------------------- 1 | import { Routes } from '@angular/router'; 2 | 3 | import activateRoute from './activate/activate.route'; 4 | import passwordRoute from './password/password.route'; 5 | import passwordResetFinishRoute from './password-reset/finish/password-reset-finish.route'; 6 | import passwordResetInitRoute from './password-reset/init/password-reset-init.route'; 7 | import registerRoute from './register/register.route'; 8 | import settingsRoute from './settings/settings.route'; 9 | 10 | const accountRoutes: Routes = [ 11 | activateRoute, 12 | passwordRoute, 13 | passwordResetFinishRoute, 14 | passwordResetInitRoute, 15 | registerRoute, 16 | settingsRoute, 17 | ]; 18 | 19 | export default accountRoutes; 20 | -------------------------------------------------------------------------------- /src/main/webapp/app/account/activate/activate.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

Activation

5 | @if (success()) { 6 |
7 | Your user account has been activated. Please 8 | sign in. 9 |
10 | } 11 | @if (error()) { 12 |
13 | Your user could not be activated. Please use the registration form to sign up. 14 |
15 | } 16 |
17 |
18 |
19 | -------------------------------------------------------------------------------- /src/main/webapp/app/account/activate/activate.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, inject, signal } from '@angular/core'; 2 | import { ActivatedRoute, RouterModule } from '@angular/router'; 3 | import { mergeMap } from 'rxjs/operators'; 4 | 5 | import SharedModule from 'app/shared/shared.module'; 6 | import { ActivateService } from './activate.service'; 7 | 8 | @Component({ 9 | selector: 'jhi-activate', 10 | imports: [SharedModule, RouterModule], 11 | templateUrl: './activate.component.html', 12 | }) 13 | export default class ActivateComponent implements OnInit { 14 | error = signal(false); 15 | success = signal(false); 16 | 17 | private readonly activateService = inject(ActivateService); 18 | private readonly route = inject(ActivatedRoute); 19 | 20 | ngOnInit(): void { 21 | this.route.queryParams.pipe(mergeMap(params => this.activateService.get(params.key))).subscribe({ 22 | next: () => this.success.set(true), 23 | error: () => this.error.set(true), 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/webapp/app/account/activate/activate.route.ts: -------------------------------------------------------------------------------- 1 | import { Route } from '@angular/router'; 2 | 3 | import ActivateComponent from './activate.component'; 4 | 5 | const activateRoute: Route = { 6 | path: 'activate', 7 | component: ActivateComponent, 8 | title: 'Activation', 9 | }; 10 | 11 | export default activateRoute; 12 | -------------------------------------------------------------------------------- /src/main/webapp/app/account/activate/activate.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, inject } from '@angular/core'; 2 | import { HttpClient, HttpParams } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | 5 | import { ApplicationConfigService } from 'app/core/config/application-config.service'; 6 | 7 | @Injectable({ providedIn: 'root' }) 8 | export class ActivateService { 9 | private readonly http = inject(HttpClient); 10 | private readonly applicationConfigService = inject(ApplicationConfigService); 11 | 12 | get(key: string): Observable<{}> { 13 | return this.http.get(this.applicationConfigService.getEndpointFor('api/activate'), { 14 | params: new HttpParams().set('key', key), 15 | }); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/webapp/app/account/password-reset/finish/password-reset-finish.route.ts: -------------------------------------------------------------------------------- 1 | import { Route } from '@angular/router'; 2 | 3 | import PasswordResetFinishComponent from './password-reset-finish.component'; 4 | 5 | const passwordResetFinishRoute: Route = { 6 | path: 'reset/finish', 7 | component: PasswordResetFinishComponent, 8 | title: 'Password', 9 | }; 10 | 11 | export default passwordResetFinishRoute; 12 | -------------------------------------------------------------------------------- /src/main/webapp/app/account/password-reset/finish/password-reset-finish.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, inject } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | 5 | import { ApplicationConfigService } from 'app/core/config/application-config.service'; 6 | 7 | @Injectable({ providedIn: 'root' }) 8 | export class PasswordResetFinishService { 9 | private readonly http = inject(HttpClient); 10 | private readonly applicationConfigService = inject(ApplicationConfigService); 11 | 12 | save(key: string, newPassword: string): Observable<{}> { 13 | return this.http.post(this.applicationConfigService.getEndpointFor('api/account/reset-password/finish'), { key, newPassword }); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/webapp/app/account/password-reset/init/password-reset-init.component.ts: -------------------------------------------------------------------------------- 1 | import { AfterViewInit, Component, ElementRef, inject, signal, viewChild } from '@angular/core'; 2 | import { FormBuilder, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; 3 | import SharedModule from 'app/shared/shared.module'; 4 | 5 | import { PasswordResetInitService } from './password-reset-init.service'; 6 | 7 | @Component({ 8 | selector: 'jhi-password-reset-init', 9 | imports: [SharedModule, FormsModule, ReactiveFormsModule], 10 | templateUrl: './password-reset-init.component.html', 11 | }) 12 | export default class PasswordResetInitComponent implements AfterViewInit { 13 | email = viewChild.required('email'); 14 | 15 | success = signal(false); 16 | resetRequestForm; 17 | 18 | private readonly passwordResetInitService = inject(PasswordResetInitService); 19 | private readonly fb = inject(FormBuilder); 20 | 21 | constructor() { 22 | this.resetRequestForm = this.fb.group({ 23 | email: ['', [Validators.required, Validators.minLength(5), Validators.maxLength(254), Validators.email]], 24 | }); 25 | } 26 | 27 | ngAfterViewInit(): void { 28 | this.email().nativeElement.focus(); 29 | } 30 | 31 | requestReset(): void { 32 | this.passwordResetInitService.save(this.resetRequestForm.get(['email'])!.value).subscribe(() => this.success.set(true)); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/webapp/app/account/password-reset/init/password-reset-init.route.ts: -------------------------------------------------------------------------------- 1 | import { Route } from '@angular/router'; 2 | 3 | import PasswordResetInitComponent from './password-reset-init.component'; 4 | 5 | const passwordResetInitRoute: Route = { 6 | path: 'reset/request', 7 | component: PasswordResetInitComponent, 8 | title: 'Password', 9 | }; 10 | 11 | export default passwordResetInitRoute; 12 | -------------------------------------------------------------------------------- /src/main/webapp/app/account/password-reset/init/password-reset-init.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, inject } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | 5 | import { ApplicationConfigService } from 'app/core/config/application-config.service'; 6 | 7 | @Injectable({ providedIn: 'root' }) 8 | export class PasswordResetInitService { 9 | private readonly http = inject(HttpClient); 10 | private readonly applicationConfigService = inject(ApplicationConfigService); 11 | 12 | save(mail: string): Observable<{}> { 13 | return this.http.post(this.applicationConfigService.getEndpointFor('api/account/reset-password/init'), mail); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/webapp/app/account/password/password-strength-bar/password-strength-bar.component.html: -------------------------------------------------------------------------------- 1 |
2 | Password strength: 3 |
    4 |
  • 5 |
  • 6 |
  • 7 |
  • 8 |
  • 9 |
10 |
11 | -------------------------------------------------------------------------------- /src/main/webapp/app/account/password/password-strength-bar/password-strength-bar.component.scss: -------------------------------------------------------------------------------- 1 | /* ========================================================================== 2 | start Password strength bar style 3 | ========================================================================== */ 4 | ul#strength { 5 | display: inline; 6 | list-style: none; 7 | margin: 0 0 0 15px; 8 | padding: 0; 9 | vertical-align: 2px; 10 | } 11 | 12 | .point { 13 | background: #ddd; 14 | border-radius: 2px; 15 | display: inline-block; 16 | height: 5px; 17 | margin-right: 1px; 18 | width: 20px; 19 | &:last-child { 20 | margin: 0 !important; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/webapp/app/account/password/password.route.ts: -------------------------------------------------------------------------------- 1 | import { Route } from '@angular/router'; 2 | 3 | import { UserRouteAccessService } from 'app/core/auth/user-route-access.service'; 4 | import PasswordComponent from './password.component'; 5 | 6 | const passwordRoute: Route = { 7 | path: 'password', 8 | component: PasswordComponent, 9 | title: 'Password', 10 | canActivate: [UserRouteAccessService], 11 | }; 12 | 13 | export default passwordRoute; 14 | -------------------------------------------------------------------------------- /src/main/webapp/app/account/password/password.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, inject } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | 5 | import { ApplicationConfigService } from 'app/core/config/application-config.service'; 6 | 7 | @Injectable({ providedIn: 'root' }) 8 | export class PasswordService { 9 | private readonly http = inject(HttpClient); 10 | private readonly applicationConfigService = inject(ApplicationConfigService); 11 | 12 | save(newPassword: string, currentPassword: string): Observable<{}> { 13 | return this.http.post(this.applicationConfigService.getEndpointFor('api/account/change-password'), { currentPassword, newPassword }); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/webapp/app/account/register/register.model.ts: -------------------------------------------------------------------------------- 1 | export class Registration { 2 | constructor( 3 | public login: string, 4 | public email: string, 5 | public password: string, 6 | public langKey: string, 7 | ) {} 8 | } 9 | -------------------------------------------------------------------------------- /src/main/webapp/app/account/register/register.route.ts: -------------------------------------------------------------------------------- 1 | import { Route } from '@angular/router'; 2 | 3 | import RegisterComponent from './register.component'; 4 | 5 | const registerRoute: Route = { 6 | path: 'register', 7 | component: RegisterComponent, 8 | title: 'Registration', 9 | }; 10 | 11 | export default registerRoute; 12 | -------------------------------------------------------------------------------- /src/main/webapp/app/account/register/register.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, inject } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | 5 | import { ApplicationConfigService } from 'app/core/config/application-config.service'; 6 | import { Registration } from './register.model'; 7 | 8 | @Injectable({ providedIn: 'root' }) 9 | export class RegisterService { 10 | private readonly http = inject(HttpClient); 11 | private readonly applicationConfigService = inject(ApplicationConfigService); 12 | 13 | save(registration: Registration): Observable<{}> { 14 | return this.http.post(this.applicationConfigService.getEndpointFor('api/register'), registration); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/webapp/app/account/settings/settings.route.ts: -------------------------------------------------------------------------------- 1 | import { Route } from '@angular/router'; 2 | 3 | import { UserRouteAccessService } from 'app/core/auth/user-route-access.service'; 4 | import SettingsComponent from './settings.component'; 5 | 6 | const settingsRoute: Route = { 7 | path: 'settings', 8 | component: SettingsComponent, 9 | title: 'Settings', 10 | canActivate: [UserRouteAccessService], 11 | }; 12 | 13 | export default settingsRoute; 14 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/admin.routes.ts: -------------------------------------------------------------------------------- 1 | import { Routes } from '@angular/router'; 2 | /* jhipster-needle-add-admin-module-import - JHipster will add admin modules imports here */ 3 | 4 | const routes: Routes = [ 5 | { 6 | path: 'user-management', 7 | loadChildren: () => import('./user-management/user-management.route'), 8 | title: 'userManagement.home.title', 9 | }, 10 | { 11 | path: 'docs', 12 | loadComponent: () => import('./docs/docs.component'), 13 | title: 'global.menu.admin.apidocs', 14 | }, 15 | { 16 | path: 'configuration', 17 | loadComponent: () => import('./configuration/configuration.component'), 18 | title: 'configuration.title', 19 | }, 20 | { 21 | path: 'health', 22 | loadComponent: () => import('./health/health.component'), 23 | title: 'health.title', 24 | }, 25 | { 26 | path: 'logs', 27 | loadComponent: () => import('./logs/logs.component'), 28 | title: 'logs.title', 29 | }, 30 | { 31 | path: 'metrics', 32 | loadComponent: () => import('./metrics/metrics.component'), 33 | title: 'metrics.title', 34 | }, 35 | { 36 | path: 'gateway', 37 | loadComponent: () => import('./gateway/gateway.component'), 38 | title: 'gateway.title', 39 | }, 40 | /* jhipster-needle-add-admin-route - JHipster will add admin routes here */ 41 | ]; 42 | 43 | export default routes; 44 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/configuration/configuration.model.ts: -------------------------------------------------------------------------------- 1 | export interface ConfigProps { 2 | contexts: Contexts; 3 | } 4 | 5 | export type Contexts = Record; 6 | 7 | export interface Context { 8 | beans: Beans; 9 | parentId?: any; 10 | } 11 | 12 | export type Beans = Record; 13 | 14 | export interface Bean { 15 | prefix: string; 16 | properties: any; 17 | } 18 | 19 | export interface Env { 20 | activeProfiles?: string[]; 21 | propertySources: PropertySource[]; 22 | } 23 | 24 | export interface PropertySource { 25 | name: string; 26 | properties: Properties; 27 | } 28 | 29 | export type Properties = Record; 30 | 31 | export interface Property { 32 | value: string; 33 | origin?: string; 34 | } 35 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/configuration/configuration.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, inject } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | import { map } from 'rxjs/operators'; 5 | 6 | import { ApplicationConfigService } from 'app/core/config/application-config.service'; 7 | import { Bean, Beans, ConfigProps, Env, PropertySource } from './configuration.model'; 8 | 9 | @Injectable({ providedIn: 'root' }) 10 | export class ConfigurationService { 11 | private readonly http = inject(HttpClient); 12 | private readonly applicationConfigService = inject(ApplicationConfigService); 13 | 14 | getBeans(): Observable { 15 | return this.http.get(this.applicationConfigService.getEndpointFor('management/configprops')).pipe( 16 | map(configProps => 17 | Object.values( 18 | Object.values(configProps.contexts) 19 | .map(context => context.beans) 20 | .reduce((allBeans: Beans, contextBeans: Beans) => ({ ...allBeans, ...contextBeans }), {}), 21 | ), 22 | ), 23 | ); 24 | } 25 | 26 | getPropertySources(): Observable { 27 | return this.http.get(this.applicationConfigService.getEndpointFor('management/env')).pipe(map(env => env.propertySources)); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/docs/docs.component.html: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/docs/docs.component.scss: -------------------------------------------------------------------------------- 1 | @import 'bootstrap/scss/functions'; 2 | @import 'bootstrap/scss/variables'; 3 | 4 | iframe { 5 | background: white; 6 | } 7 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/docs/docs.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'jhi-docs', 5 | templateUrl: './docs.component.html', 6 | styleUrl: './docs.component.scss', 7 | }) 8 | export default class DocsComponent {} 9 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/gateway/gateway-route.model.ts: -------------------------------------------------------------------------------- 1 | export class GatewayRoute { 2 | constructor( 3 | public path: string, 4 | public serviceId: string, 5 | public serviceInstances: any[], 6 | ) {} 7 | } 8 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/gateway/gateway-routes.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, inject } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | 5 | import { ApplicationConfigService } from 'app/core/config/application-config.service'; 6 | import { GatewayRoute } from './gateway-route.model'; 7 | 8 | @Injectable() 9 | export class GatewayRoutesService { 10 | private readonly http = inject(HttpClient); 11 | private readonly applicationConfigService = inject(ApplicationConfigService); 12 | 13 | findAll(): Observable { 14 | return this.http.get(this.applicationConfigService.getEndpointFor('api/gateway/routes')); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/health/health.model.ts: -------------------------------------------------------------------------------- 1 | export type HealthStatus = 'UP' | 'DOWN' | 'UNKNOWN' | 'OUT_OF_SERVICE'; 2 | 3 | export type HealthKey = 4 | | 'discoveryComposite' 5 | | 'refreshScope' 6 | | 'clientConfigServer' 7 | | 'hystrix' 8 | | 'consul' 9 | | 'diskSpace' 10 | | 'mail' 11 | | 'ping' 12 | | 'livenessState' 13 | | 'readinessState' 14 | | 'r2dbc'; 15 | 16 | export interface Health { 17 | status: HealthStatus; 18 | components?: Partial>; 19 | } 20 | 21 | export interface HealthDetails { 22 | status: HealthStatus; 23 | details?: Record; 24 | } 25 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/health/health.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, inject } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | 5 | import { ApplicationConfigService } from 'app/core/config/application-config.service'; 6 | import { Health } from './health.model'; 7 | 8 | @Injectable({ providedIn: 'root' }) 9 | export class HealthService { 10 | private readonly http = inject(HttpClient); 11 | private readonly applicationConfigService = inject(ApplicationConfigService); 12 | 13 | checkHealth(): Observable { 14 | return this.http.get(this.applicationConfigService.getEndpointFor('management/health')); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/health/modal/health-modal.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, inject } from '@angular/core'; 2 | import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; 3 | 4 | import SharedModule from 'app/shared/shared.module'; 5 | import { HealthDetails, HealthKey } from '../health.model'; 6 | 7 | @Component({ 8 | selector: 'jhi-health-modal', 9 | templateUrl: './health-modal.component.html', 10 | imports: [SharedModule], 11 | }) 12 | export default class HealthModalComponent { 13 | health?: { key: HealthKey; value: HealthDetails }; 14 | 15 | private readonly activeModal = inject(NgbActiveModal); 16 | 17 | readableValue(value: any): string { 18 | if (this.health?.key === 'diskSpace') { 19 | // should display storage space in a human readable unit 20 | const val = value / 1073741824; 21 | if (val > 1) { 22 | return `${val.toFixed(2)} GB`; 23 | } 24 | return `${(value / 1048576).toFixed(2)} MB`; 25 | } 26 | 27 | if (typeof value === 'object') { 28 | return JSON.stringify(value); 29 | } 30 | return String(value); 31 | } 32 | 33 | dismiss(): void { 34 | this.activeModal.dismiss(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/logs/log.model.ts: -------------------------------------------------------------------------------- 1 | export type Level = 'TRACE' | 'DEBUG' | 'INFO' | 'WARN' | 'ERROR' | 'OFF'; 2 | 3 | export interface Logger { 4 | configuredLevel: Level | null; 5 | effectiveLevel: Level; 6 | } 7 | 8 | export interface LoggersResponse { 9 | levels: Level[]; 10 | loggers: Record; 11 | } 12 | 13 | export class Log { 14 | constructor( 15 | public name: string, 16 | public level: Level, 17 | ) {} 18 | } 19 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/logs/logs.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing'; 3 | import { provideHttpClient } from '@angular/common/http'; 4 | 5 | import { LogsService } from './logs.service'; 6 | 7 | describe('Logs Service', () => { 8 | let service: LogsService; 9 | let httpMock: HttpTestingController; 10 | 11 | beforeEach(() => { 12 | TestBed.configureTestingModule({ 13 | providers: [provideHttpClient(), provideHttpClientTesting()], 14 | }); 15 | 16 | service = TestBed.inject(LogsService); 17 | httpMock = TestBed.inject(HttpTestingController); 18 | }); 19 | 20 | afterEach(() => { 21 | httpMock.verify(); 22 | }); 23 | 24 | describe('Service methods', () => { 25 | it('should change log level', () => { 26 | service.changeLevel('main', 'ERROR').subscribe(); 27 | 28 | const req = httpMock.expectOne({ method: 'POST' }); 29 | expect(req.request.body).toEqual({ configuredLevel: 'ERROR' }); 30 | }); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/logs/logs.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, inject } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | 5 | import { ApplicationConfigService } from 'app/core/config/application-config.service'; 6 | import { Level, LoggersResponse } from './log.model'; 7 | 8 | @Injectable({ providedIn: 'root' }) 9 | export class LogsService { 10 | private readonly http = inject(HttpClient); 11 | private readonly applicationConfigService = inject(ApplicationConfigService); 12 | 13 | changeLevel(name: string, configuredLevel: Level, service?: string): Observable<{}> { 14 | return this.http.post(this.applicationConfigService.getEndpointFor(`management/loggers/${name}`, service), { configuredLevel }); 15 | } 16 | 17 | findAll(service?: string): Observable { 18 | return this.http.get(this.applicationConfigService.getEndpointFor('management/loggers', service)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/metrics/blocks/jvm-memory/jvm-memory.component.html: -------------------------------------------------------------------------------- 1 |

Memory

2 | 3 | @if (!updating() && jvmMemoryMetrics()) { 4 |
5 | @for (entry of jvmMemoryMetrics() | keyvalue; track $index) { 6 |
7 | @if (entry.value.max !== -1) { 8 | 9 | {{ entry.key }} 10 | ({{ entry.value.used / 1048576 | number: '1.0-0' }}M / {{ entry.value.max / 1048576 | number: '1.0-0' }}M) 11 | 12 | 13 |
Committed : {{ entry.value.committed / 1048576 | number: '1.0-0' }}M
14 | 15 | {{ (entry.value.used * 100) / entry.value.max | number: '1.0-0' }}% 16 | 17 | } @else { 18 | {{ entry.key }} {{ entry.value.used / 1048576 | number: '1.0-0' }}M 21 | } 22 |
23 | } 24 |
25 | } 26 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/metrics/blocks/jvm-memory/jvm-memory.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, input } from '@angular/core'; 2 | 3 | import SharedModule from 'app/shared/shared.module'; 4 | import { JvmMetrics } from 'app/admin/metrics/metrics.model'; 5 | 6 | @Component({ 7 | selector: 'jhi-jvm-memory', 8 | templateUrl: './jvm-memory.component.html', 9 | imports: [SharedModule], 10 | }) 11 | export class JvmMemoryComponent { 12 | /** 13 | * object containing all jvm memory metrics 14 | */ 15 | jvmMemoryMetrics = input>(); 16 | 17 | /** 18 | * boolean field saying if the metrics are in the process of being updated 19 | */ 20 | updating = input(); 21 | } 22 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/metrics/blocks/metrics-cache/metrics-cache.component.ts: -------------------------------------------------------------------------------- 1 | import { ChangeDetectionStrategy, Component, input } from '@angular/core'; 2 | 3 | import SharedModule from 'app/shared/shared.module'; 4 | import { CacheMetrics } from 'app/admin/metrics/metrics.model'; 5 | import { filterNaN } from 'app/core/util/operators'; 6 | 7 | @Component({ 8 | selector: 'jhi-metrics-cache', 9 | templateUrl: './metrics-cache.component.html', 10 | changeDetection: ChangeDetectionStrategy.OnPush, 11 | imports: [SharedModule], 12 | }) 13 | export class MetricsCacheComponent { 14 | /** 15 | * object containing all cache related metrics 16 | */ 17 | cacheMetrics = input>(); 18 | 19 | /** 20 | * boolean field saying if the metrics are in the process of being updated 21 | */ 22 | updating = input(); 23 | 24 | filterNaN = (n: number): number => filterNaN(n); 25 | } 26 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/metrics/blocks/metrics-datasource/metrics-datasource.component.ts: -------------------------------------------------------------------------------- 1 | import { ChangeDetectionStrategy, Component, input } from '@angular/core'; 2 | 3 | import SharedModule from 'app/shared/shared.module'; 4 | import { Databases } from 'app/admin/metrics/metrics.model'; 5 | import { filterNaN } from 'app/core/util/operators'; 6 | 7 | @Component({ 8 | selector: 'jhi-metrics-datasource', 9 | templateUrl: './metrics-datasource.component.html', 10 | changeDetection: ChangeDetectionStrategy.OnPush, 11 | imports: [SharedModule], 12 | }) 13 | export class MetricsDatasourceComponent { 14 | /** 15 | * object containing all datasource related metrics 16 | */ 17 | datasourceMetrics = input(); 18 | 19 | /** 20 | * boolean field saying if the metrics are in the process of being updated 21 | */ 22 | updating = input(); 23 | 24 | filterNaN = (n: number): number => filterNaN(n); 25 | } 26 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/metrics/blocks/metrics-endpoints-requests/metrics-endpoints-requests.component.html: -------------------------------------------------------------------------------- 1 |

Endpoints requests (time in millisecond)

2 | 3 | @if (!updating() && endpointsRequestsMetrics()) { 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | @for (entry of endpointsRequestsMetrics() | keyvalue; track entry.key) { 16 | @for (method of entry.value | keyvalue; track method.key) { 17 | 18 | 19 | 20 | 21 | 22 | 23 | } 24 | } 25 | 26 |
MethodEndpoint urlCountMean
{{ method.key }}{{ entry.key }}{{ method.value!.count }}{{ method.value!.mean | number: '1.0-3' }}
27 |
28 | } 29 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/metrics/blocks/metrics-endpoints-requests/metrics-endpoints-requests.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, input } from '@angular/core'; 2 | 3 | import SharedModule from 'app/shared/shared.module'; 4 | import { Services } from 'app/admin/metrics/metrics.model'; 5 | 6 | @Component({ 7 | selector: 'jhi-metrics-endpoints-requests', 8 | templateUrl: './metrics-endpoints-requests.component.html', 9 | imports: [SharedModule], 10 | }) 11 | export class MetricsEndpointsRequestsComponent { 12 | /** 13 | * object containing service related metrics 14 | */ 15 | endpointsRequestsMetrics = input(); 16 | 17 | /** 18 | * boolean field saying if the metrics are in the process of being updated 19 | */ 20 | updating = input(); 21 | } 22 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/metrics/blocks/metrics-garbagecollector/metrics-garbagecollector.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, input } from '@angular/core'; 2 | 3 | import SharedModule from 'app/shared/shared.module'; 4 | import { GarbageCollector } from 'app/admin/metrics/metrics.model'; 5 | 6 | @Component({ 7 | selector: 'jhi-metrics-garbagecollector', 8 | templateUrl: './metrics-garbagecollector.component.html', 9 | imports: [SharedModule], 10 | }) 11 | export class MetricsGarbageCollectorComponent { 12 | /** 13 | * object containing garbage collector related metrics 14 | */ 15 | garbageCollectorMetrics = input(); 16 | 17 | /** 18 | * boolean field saying if the metrics are in the process of being updated 19 | */ 20 | updating = input(); 21 | } 22 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/metrics/blocks/metrics-request/metrics-request.component.html: -------------------------------------------------------------------------------- 1 |

HTTP requests (time in millisecond)

2 | 3 | @let requestMetricsRef = requestMetrics(); 4 | @if (!updating() && requestMetricsRef) { 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | @for (entry of requestMetricsRef['percode'] | keyvalue; track entry.key) { 16 | 17 | 18 | 29 | 32 | 33 | 34 | } 35 | 36 |
CodeCountMeanMax
{{ entry.key }} 19 | 26 | {{ entry.value.count }} 27 | 28 | 30 | {{ filterNaN(entry.value.mean) | number: '1.0-2' }} 31 | {{ entry.value.max | number: '1.0-2' }}
37 | } 38 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/metrics/blocks/metrics-request/metrics-request.component.ts: -------------------------------------------------------------------------------- 1 | import { ChangeDetectionStrategy, Component, input } from '@angular/core'; 2 | 3 | import SharedModule from 'app/shared/shared.module'; 4 | import { HttpServerRequests } from 'app/admin/metrics/metrics.model'; 5 | import { filterNaN } from 'app/core/util/operators'; 6 | 7 | @Component({ 8 | selector: 'jhi-metrics-request', 9 | templateUrl: './metrics-request.component.html', 10 | changeDetection: ChangeDetectionStrategy.OnPush, 11 | imports: [SharedModule], 12 | }) 13 | export class MetricsRequestComponent { 14 | /** 15 | * object containing http request related metrics 16 | */ 17 | requestMetrics = input(); 18 | 19 | /** 20 | * boolean field saying if the metrics are in the process of being updated 21 | */ 22 | updating = input(); 23 | 24 | filterNaN = (n: number): number => filterNaN(n); 25 | } 26 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/metrics/blocks/metrics-system/metrics-system.component.ts: -------------------------------------------------------------------------------- 1 | import { ChangeDetectionStrategy, Component, input } from '@angular/core'; 2 | 3 | import SharedModule from 'app/shared/shared.module'; 4 | import { ProcessMetrics } from 'app/admin/metrics/metrics.model'; 5 | 6 | @Component({ 7 | selector: 'jhi-metrics-system', 8 | templateUrl: './metrics-system.component.html', 9 | changeDetection: ChangeDetectionStrategy.OnPush, 10 | imports: [SharedModule], 11 | }) 12 | export class MetricsSystemComponent { 13 | /** 14 | * object containing thread related metrics 15 | */ 16 | systemMetrics = input(); 17 | 18 | /** 19 | * boolean field saying if the metrics are in the process of being updated 20 | */ 21 | updating = input(); 22 | 23 | convertMillisecondsToDuration(ms: number): string { 24 | const times = { 25 | year: 31557600000, 26 | month: 2629746000, 27 | day: 86400000, 28 | hour: 3600000, 29 | minute: 60000, 30 | second: 1000, 31 | }; 32 | let timeString = ''; 33 | for (const [key, value] of Object.entries(times)) { 34 | if (Math.floor(ms / value) > 0) { 35 | let plural = ''; 36 | if (Math.floor(ms / value) > 1) { 37 | plural = 's'; 38 | } 39 | timeString += `${Math.floor(ms / value).toString()} ${key.toString()}${plural} `; 40 | ms = ms - value * Math.floor(ms / value); 41 | } 42 | } 43 | return timeString; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/metrics/metrics.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, inject } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | 5 | import { ApplicationConfigService } from 'app/core/config/application-config.service'; 6 | import { Metrics, ThreadDump } from './metrics.model'; 7 | 8 | @Injectable({ providedIn: 'root' }) 9 | export class MetricsService { 10 | private readonly http = inject(HttpClient); 11 | private readonly applicationConfigService = inject(ApplicationConfigService); 12 | 13 | getMetrics(): Observable { 14 | return this.http.get(this.applicationConfigService.getEndpointFor('management/jhimetrics')); 15 | } 16 | 17 | threadDump(): Observable { 18 | return this.http.get(this.applicationConfigService.getEndpointFor('management/threaddump')); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/user-management/delete/user-management-delete-dialog.component.html: -------------------------------------------------------------------------------- 1 | @if (user) { 2 |
3 | 6 | 7 | 12 | 13 | 20 |
21 | } 22 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/user-management/delete/user-management-delete-dialog.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, inject } from '@angular/core'; 2 | import { FormsModule } from '@angular/forms'; 3 | import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; 4 | 5 | import SharedModule from 'app/shared/shared.module'; 6 | import { User } from '../user-management.model'; 7 | import { UserManagementService } from '../service/user-management.service'; 8 | 9 | @Component({ 10 | selector: 'jhi-user-mgmt-delete-dialog', 11 | templateUrl: './user-management-delete-dialog.component.html', 12 | imports: [SharedModule, FormsModule], 13 | }) 14 | export default class UserManagementDeleteDialogComponent { 15 | user?: User; 16 | 17 | private readonly userService = inject(UserManagementService); 18 | private readonly activeModal = inject(NgbActiveModal); 19 | 20 | cancel(): void { 21 | this.activeModal.dismiss(); 22 | } 23 | 24 | confirmDelete(login: string): void { 25 | this.userService.delete(login).subscribe(() => { 26 | this.activeModal.close('deleted'); 27 | }); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/user-management/detail/user-management-detail.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, input } from '@angular/core'; 2 | import { RouterModule } from '@angular/router'; 3 | import SharedModule from 'app/shared/shared.module'; 4 | 5 | import { User } from '../user-management.model'; 6 | 7 | @Component({ 8 | selector: 'jhi-user-mgmt-detail', 9 | templateUrl: './user-management-detail.component.html', 10 | imports: [RouterModule, SharedModule], 11 | }) 12 | export default class UserManagementDetailComponent { 13 | user = input(null); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/user-management/user-management.model.ts: -------------------------------------------------------------------------------- 1 | export interface IUser { 2 | id: number | null; 3 | login?: string; 4 | firstName?: string | null; 5 | lastName?: string | null; 6 | email?: string; 7 | activated?: boolean; 8 | langKey?: string; 9 | authorities?: string[]; 10 | createdBy?: string; 11 | createdDate?: Date; 12 | lastModifiedBy?: string; 13 | lastModifiedDate?: Date; 14 | } 15 | 16 | export class User implements IUser { 17 | constructor( 18 | public id: number | null, 19 | public login?: string, 20 | public firstName?: string | null, 21 | public lastName?: string | null, 22 | public email?: string, 23 | public activated?: boolean, 24 | public langKey?: string, 25 | public authorities?: string[], 26 | public createdBy?: string, 27 | public createdDate?: Date, 28 | public lastModifiedBy?: string, 29 | public lastModifiedDate?: Date, 30 | ) {} 31 | } 32 | -------------------------------------------------------------------------------- /src/main/webapp/app/admin/user-management/user-management.route.ts: -------------------------------------------------------------------------------- 1 | import { inject } from '@angular/core'; 2 | import { ActivatedRouteSnapshot, ResolveFn, Routes } from '@angular/router'; 3 | import { of } from 'rxjs'; 4 | 5 | import { IUser } from './user-management.model'; 6 | import { UserManagementService } from './service/user-management.service'; 7 | 8 | export const userManagementResolve: ResolveFn = (route: ActivatedRouteSnapshot) => { 9 | const login = route.paramMap.get('login'); 10 | if (login) { 11 | return inject(UserManagementService).find(login); 12 | } 13 | return of(null); 14 | }; 15 | 16 | const userManagementRoute: Routes = [ 17 | { 18 | path: '', 19 | loadComponent: () => import('./list/user-management.component'), 20 | data: { 21 | defaultSort: 'id,asc', 22 | }, 23 | }, 24 | { 25 | path: ':login/view', 26 | loadComponent: () => import('./detail/user-management-detail.component'), 27 | resolve: { 28 | user: userManagementResolve, 29 | }, 30 | }, 31 | { 32 | path: 'new', 33 | loadComponent: () => import('./update/user-management-update.component'), 34 | resolve: { 35 | user: userManagementResolve, 36 | }, 37 | }, 38 | { 39 | path: ':login/edit', 40 | loadComponent: () => import('./update/user-management-update.component'), 41 | resolve: { 42 | user: userManagementResolve, 43 | }, 44 | }, 45 | ]; 46 | 47 | export default userManagementRoute; 48 | -------------------------------------------------------------------------------- /src/main/webapp/app/app-page-title-strategy.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { RouterStateSnapshot, TitleStrategy } from '@angular/router'; 3 | 4 | @Injectable() 5 | export class AppPageTitleStrategy extends TitleStrategy { 6 | override updateTitle(routerState: RouterStateSnapshot): void { 7 | let pageTitle = this.buildTitle(routerState); 8 | pageTitle ??= 'Jhipster Sample Gateway'; 9 | document.title = pageTitle; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/webapp/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, inject } from '@angular/core'; 2 | import { registerLocaleData } from '@angular/common'; 3 | import dayjs from 'dayjs/esm'; 4 | import { FaIconLibrary } from '@fortawesome/angular-fontawesome'; 5 | import { NgbDatepickerConfig } from '@ng-bootstrap/ng-bootstrap'; 6 | import locale from '@angular/common/locales/en'; 7 | // jhipster-needle-angular-add-module-import JHipster will add new module here 8 | 9 | import { ApplicationConfigService } from 'app/core/config/application-config.service'; 10 | import { fontAwesomeIcons } from './config/font-awesome-icons'; 11 | import MainComponent from './layouts/main/main.component'; 12 | 13 | @Component({ 14 | selector: 'jhi-app', 15 | template: '', 16 | imports: [ 17 | MainComponent, 18 | // jhipster-needle-angular-add-module JHipster will add new module here 19 | ], 20 | }) 21 | export default class AppComponent { 22 | private readonly applicationConfigService = inject(ApplicationConfigService); 23 | private readonly iconLibrary = inject(FaIconLibrary); 24 | private readonly dpConfig = inject(NgbDatepickerConfig); 25 | 26 | constructor() { 27 | this.applicationConfigService.setEndpointPrefix(SERVER_API_URL); 28 | registerLocaleData(locale); 29 | this.iconLibrary.addIcons(...fontAwesomeIcons); 30 | this.dpConfig.minDate = { year: dayjs().subtract(100, 'year').year(), month: 1, day: 1 }; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/webapp/app/app.routes.ts: -------------------------------------------------------------------------------- 1 | import { Routes } from '@angular/router'; 2 | 3 | import { Authority } from 'app/config/authority.constants'; 4 | 5 | import { UserRouteAccessService } from 'app/core/auth/user-route-access.service'; 6 | import { errorRoute } from './layouts/error/error.route'; 7 | 8 | const routes: Routes = [ 9 | { 10 | path: '', 11 | loadComponent: () => import('./home/home.component'), 12 | title: 'home.title', 13 | }, 14 | { 15 | path: '', 16 | loadComponent: () => import('./layouts/navbar/navbar.component'), 17 | outlet: 'navbar', 18 | }, 19 | { 20 | path: 'admin', 21 | data: { 22 | authorities: [Authority.ADMIN], 23 | }, 24 | canActivate: [UserRouteAccessService], 25 | loadChildren: () => import('./admin/admin.routes'), 26 | }, 27 | { 28 | path: 'account', 29 | loadChildren: () => import('./account/account.route'), 30 | }, 31 | { 32 | path: 'login', 33 | loadComponent: () => import('./login/login.component'), 34 | title: 'login.title', 35 | }, 36 | { 37 | path: '', 38 | loadChildren: () => import(`./entities/entity.routes`), 39 | }, 40 | ...errorRoute, 41 | ]; 42 | 43 | export default routes; 44 | -------------------------------------------------------------------------------- /src/main/webapp/app/config/authority.constants.ts: -------------------------------------------------------------------------------- 1 | export enum Authority { 2 | ADMIN = 'ROLE_ADMIN', 3 | USER = 'ROLE_USER', 4 | } 5 | -------------------------------------------------------------------------------- /src/main/webapp/app/config/datepicker-adapter.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Angular bootstrap Date adapter 3 | */ 4 | import { Injectable } from '@angular/core'; 5 | import { NgbDateAdapter, NgbDateStruct } from '@ng-bootstrap/ng-bootstrap'; 6 | import dayjs from 'dayjs/esm'; 7 | 8 | @Injectable() 9 | export class NgbDateDayjsAdapter extends NgbDateAdapter { 10 | fromModel(date: dayjs.Dayjs | null): NgbDateStruct | null { 11 | if (date && dayjs.isDayjs(date) && date.isValid()) { 12 | return { year: date.year(), month: date.month() + 1, day: date.date() }; 13 | } 14 | return null; 15 | } 16 | 17 | toModel(date: NgbDateStruct | null): dayjs.Dayjs | null { 18 | return date ? dayjs(`${date.year}-${date.month}-${date.day}`) : null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/webapp/app/config/dayjs.ts: -------------------------------------------------------------------------------- 1 | import dayjs from 'dayjs/esm'; 2 | import customParseFormat from 'dayjs/esm/plugin/customParseFormat'; 3 | import duration from 'dayjs/esm/plugin/duration'; 4 | import relativeTime from 'dayjs/esm/plugin/relativeTime'; 5 | 6 | // jhipster-needle-i18n-language-dayjs-imports - JHipster will import languages from dayjs here 7 | 8 | // DAYJS CONFIGURATION 9 | dayjs.extend(customParseFormat); 10 | dayjs.extend(duration); 11 | dayjs.extend(relativeTime); 12 | -------------------------------------------------------------------------------- /src/main/webapp/app/config/error.constants.ts: -------------------------------------------------------------------------------- 1 | export const PROBLEM_BASE_URL = 'https://www.jhipster.tech/problem'; 2 | export const EMAIL_ALREADY_USED_TYPE = `${PROBLEM_BASE_URL}/email-already-used`; 3 | export const LOGIN_ALREADY_USED_TYPE = `${PROBLEM_BASE_URL}/login-already-used`; 4 | -------------------------------------------------------------------------------- /src/main/webapp/app/config/font-awesome-icons.ts: -------------------------------------------------------------------------------- 1 | import { 2 | faArrowLeft, 3 | faAsterisk, 4 | faBan, 5 | faBars, 6 | faBell, 7 | faBook, 8 | faCalendarAlt, 9 | faCheck, 10 | faCloud, 11 | faCogs, 12 | faDatabase, 13 | faEye, 14 | faFlag, 15 | faHeart, 16 | faHome, 17 | faList, 18 | faLock, 19 | faPencilAlt, 20 | faPlus, 21 | faRoad, 22 | faSave, 23 | faSearch, 24 | faSignInAlt, 25 | faSignOutAlt, 26 | faSort, 27 | faSortDown, 28 | faSortUp, 29 | faSync, 30 | faTachometerAlt, 31 | faTasks, 32 | faThList, 33 | faTimes, 34 | faTrashAlt, 35 | faUser, 36 | faUserPlus, 37 | faUsers, 38 | faUsersCog, 39 | faWrench, 40 | // jhipster-needle-add-icon-import 41 | } from '@fortawesome/free-solid-svg-icons'; 42 | 43 | export const fontAwesomeIcons = [ 44 | faArrowLeft, 45 | faAsterisk, 46 | faBan, 47 | faBars, 48 | faBell, 49 | faBook, 50 | faCalendarAlt, 51 | faCheck, 52 | faCloud, 53 | faCogs, 54 | faDatabase, 55 | faEye, 56 | faFlag, 57 | faHeart, 58 | faHome, 59 | faList, 60 | faLock, 61 | faPencilAlt, 62 | faPlus, 63 | faRoad, 64 | faSave, 65 | faSearch, 66 | faSignOutAlt, 67 | faSignInAlt, 68 | faSort, 69 | faSortDown, 70 | faSortUp, 71 | faSync, 72 | faTachometerAlt, 73 | faTasks, 74 | faThList, 75 | faTimes, 76 | faTrashAlt, 77 | faUser, 78 | faUserPlus, 79 | faUsers, 80 | faUsersCog, 81 | faWrench, 82 | // jhipster-needle-add-icon-import 83 | ]; 84 | -------------------------------------------------------------------------------- /src/main/webapp/app/config/input.constants.ts: -------------------------------------------------------------------------------- 1 | export const DATE_FORMAT = 'YYYY-MM-DD'; 2 | export const DATE_TIME_FORMAT = 'YYYY-MM-DDTHH:mm'; 3 | -------------------------------------------------------------------------------- /src/main/webapp/app/config/navigation.constants.ts: -------------------------------------------------------------------------------- 1 | export const ASC = 'asc'; 2 | export const DESC = 'desc'; 3 | export const SORT = 'sort'; 4 | export const ITEM_DELETED_EVENT = 'deleted'; 5 | export const DEFAULT_SORT_DATA = 'defaultSort'; 6 | -------------------------------------------------------------------------------- /src/main/webapp/app/config/pagination.constants.ts: -------------------------------------------------------------------------------- 1 | export const TOTAL_COUNT_RESPONSE_HEADER = 'X-Total-Count'; 2 | export const PAGE_HEADER = 'page'; 3 | export const ITEMS_PER_PAGE = 20; 4 | -------------------------------------------------------------------------------- /src/main/webapp/app/config/uib-pagination.config.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, inject } from '@angular/core'; 2 | import { NgbPaginationConfig } from '@ng-bootstrap/ng-bootstrap'; 3 | 4 | import { ITEMS_PER_PAGE } from 'app/config/pagination.constants'; 5 | 6 | @Injectable({ providedIn: 'root' }) 7 | export class PaginationConfig { 8 | private readonly config = inject(NgbPaginationConfig); 9 | constructor() { 10 | this.config.boundaryLinks = true; 11 | this.config.maxSize = 5; 12 | this.config.pageSize = ITEMS_PER_PAGE; 13 | this.config.size = 'sm'; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/webapp/app/core/auth/account.model.ts: -------------------------------------------------------------------------------- 1 | export class Account { 2 | constructor( 3 | public activated: boolean, 4 | public authorities: string[], 5 | public email: string, 6 | public firstName: string | null, 7 | public langKey: string, 8 | public lastName: string | null, 9 | public login: string, 10 | public imageUrl: string | null, 11 | ) {} 12 | } 13 | -------------------------------------------------------------------------------- /src/main/webapp/app/core/auth/auth-jwt.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, inject } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | import { map } from 'rxjs/operators'; 5 | 6 | import { Login } from 'app/login/login.model'; 7 | import { ApplicationConfigService } from '../config/application-config.service'; 8 | import { StateStorageService } from './state-storage.service'; 9 | 10 | type JwtToken = { 11 | id_token: string; 12 | }; 13 | 14 | @Injectable({ providedIn: 'root' }) 15 | export class AuthServerProvider { 16 | private readonly http = inject(HttpClient); 17 | private readonly stateStorageService = inject(StateStorageService); 18 | private readonly applicationConfigService = inject(ApplicationConfigService); 19 | 20 | getToken(): string { 21 | return this.stateStorageService.getAuthenticationToken() ?? ''; 22 | } 23 | 24 | login(credentials: Login): Observable { 25 | return this.http 26 | .post(this.applicationConfigService.getEndpointFor('api/authenticate'), credentials) 27 | .pipe(map(response => this.authenticateSuccess(response, credentials.rememberMe))); 28 | } 29 | 30 | logout(): Observable { 31 | return new Observable(observer => { 32 | this.stateStorageService.clearAuthenticationToken(); 33 | observer.complete(); 34 | }); 35 | } 36 | 37 | private authenticateSuccess(response: JwtToken, rememberMe: boolean): void { 38 | this.stateStorageService.storeAuthenticationToken(response.id_token, rememberMe); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/webapp/app/core/auth/user-route-access.service.ts: -------------------------------------------------------------------------------- 1 | import { inject, isDevMode } from '@angular/core'; 2 | import { ActivatedRouteSnapshot, CanActivateFn, Router, RouterStateSnapshot } from '@angular/router'; 3 | import { map } from 'rxjs/operators'; 4 | 5 | import { AccountService } from 'app/core/auth/account.service'; 6 | import { StateStorageService } from './state-storage.service'; 7 | 8 | export const UserRouteAccessService: CanActivateFn = (next: ActivatedRouteSnapshot, state: RouterStateSnapshot) => { 9 | const accountService = inject(AccountService); 10 | const router = inject(Router); 11 | const stateStorageService = inject(StateStorageService); 12 | return accountService.identity().pipe( 13 | map(account => { 14 | if (account) { 15 | const { authorities } = next.data; 16 | 17 | if (!authorities || authorities.length === 0 || accountService.hasAnyAuthority(authorities)) { 18 | return true; 19 | } 20 | 21 | if (isDevMode()) { 22 | console.error('User does not have any of the required authorities:', authorities); 23 | } 24 | router.navigate(['accessdenied']); 25 | return false; 26 | } 27 | 28 | stateStorageService.storeUrl(state.url); 29 | router.navigate(['/login']); 30 | return false; 31 | }), 32 | ); 33 | }; 34 | -------------------------------------------------------------------------------- /src/main/webapp/app/core/config/application-config.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { ApplicationConfigService } from './application-config.service'; 4 | 5 | describe('ApplicationConfigService', () => { 6 | let service: ApplicationConfigService; 7 | 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({}); 10 | service = TestBed.inject(ApplicationConfigService); 11 | }); 12 | 13 | it('should be created', () => { 14 | expect(service).toBeTruthy(); 15 | }); 16 | 17 | describe('without prefix', () => { 18 | it('should return correctly', () => { 19 | expect(service.getEndpointFor('api')).toEqual('api'); 20 | }); 21 | 22 | it('should return correctly when passing microservice', () => { 23 | expect(service.getEndpointFor('api', 'microservice')).toEqual('services/microservice/api'); 24 | }); 25 | }); 26 | 27 | describe('with prefix', () => { 28 | beforeEach(() => { 29 | service.setEndpointPrefix('prefix/'); 30 | }); 31 | 32 | it('should return correctly', () => { 33 | expect(service.getEndpointFor('api')).toEqual('prefix/api'); 34 | }); 35 | 36 | it('should return correctly when passing microservice', () => { 37 | expect(service.getEndpointFor('api', 'microservice')).toEqual('prefix/services/microservice/api'); 38 | }); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /src/main/webapp/app/core/config/application-config.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable({ 4 | providedIn: 'root', 5 | }) 6 | export class ApplicationConfigService { 7 | private endpointPrefix = ''; 8 | private microfrontend = false; 9 | 10 | setEndpointPrefix(endpointPrefix: string): void { 11 | this.endpointPrefix = endpointPrefix; 12 | } 13 | 14 | setMicrofrontend(microfrontend = true): void { 15 | this.microfrontend = microfrontend; 16 | } 17 | 18 | isMicrofrontend(): boolean { 19 | return this.microfrontend; 20 | } 21 | 22 | getEndpointFor(api: string, microservice?: string): string { 23 | if (microservice) { 24 | return `${this.endpointPrefix}services/${microservice}/${api}`; 25 | } 26 | return `${this.endpointPrefix}${api}`; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/webapp/app/core/interceptor/auth-expired.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, inject } from '@angular/core'; 2 | import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | import { tap } from 'rxjs/operators'; 5 | import { Router } from '@angular/router'; 6 | 7 | import { LoginService } from 'app/login/login.service'; 8 | import { StateStorageService } from 'app/core/auth/state-storage.service'; 9 | 10 | @Injectable() 11 | export class AuthExpiredInterceptor implements HttpInterceptor { 12 | private readonly loginService = inject(LoginService); 13 | private readonly stateStorageService = inject(StateStorageService); 14 | private readonly router = inject(Router); 15 | 16 | intercept(request: HttpRequest, next: HttpHandler): Observable> { 17 | return next.handle(request).pipe( 18 | tap({ 19 | error: (err: HttpErrorResponse) => { 20 | if (err.status === 401 && err.url && !err.url.includes('api/account')) { 21 | this.stateStorageService.storeUrl(this.router.routerState.snapshot.url); 22 | this.loginService.logout(); 23 | this.router.navigate(['/login']); 24 | } 25 | }, 26 | }), 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/webapp/app/core/interceptor/auth.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, inject } from '@angular/core'; 2 | import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | 5 | import { StateStorageService } from 'app/core/auth/state-storage.service'; 6 | import { ApplicationConfigService } from '../config/application-config.service'; 7 | 8 | @Injectable() 9 | export class AuthInterceptor implements HttpInterceptor { 10 | private readonly stateStorageService = inject(StateStorageService); 11 | private readonly applicationConfigService = inject(ApplicationConfigService); 12 | 13 | intercept(request: HttpRequest, next: HttpHandler): Observable> { 14 | const serverApiUrl = this.applicationConfigService.getEndpointFor(''); 15 | if (!request.url || (request.url.startsWith('http') && !(serverApiUrl && request.url.startsWith(serverApiUrl)))) { 16 | return next.handle(request); 17 | } 18 | 19 | const token: string | null = this.stateStorageService.getAuthenticationToken(); 20 | if (token) { 21 | request = request.clone({ 22 | setHeaders: { 23 | Authorization: `Bearer ${token}`, 24 | }, 25 | }); 26 | } 27 | return next.handle(request); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/webapp/app/core/interceptor/error-handler.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, inject } from '@angular/core'; 2 | import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | import { tap } from 'rxjs/operators'; 5 | 6 | import { EventManager, EventWithContent } from 'app/core/util/event-manager.service'; 7 | 8 | @Injectable() 9 | export class ErrorHandlerInterceptor implements HttpInterceptor { 10 | private readonly eventManager = inject(EventManager); 11 | 12 | intercept(request: HttpRequest, next: HttpHandler): Observable> { 13 | return next.handle(request).pipe( 14 | tap({ 15 | error: (err: HttpErrorResponse) => { 16 | if (!(err.status === 401 && (err.message === '' || err.url?.includes('api/account')))) { 17 | this.eventManager.broadcast(new EventWithContent('jhipsterSampleGatewayApp.httpError', err)); 18 | } 19 | }, 20 | }), 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/webapp/app/core/interceptor/index.ts: -------------------------------------------------------------------------------- 1 | import { HTTP_INTERCEPTORS } from '@angular/common/http'; 2 | 3 | import { AuthInterceptor } from 'app/core/interceptor/auth.interceptor'; 4 | import { AuthExpiredInterceptor } from 'app/core/interceptor/auth-expired.interceptor'; 5 | import { ErrorHandlerInterceptor } from 'app/core/interceptor/error-handler.interceptor'; 6 | import { NotificationInterceptor } from 'app/core/interceptor/notification.interceptor'; 7 | 8 | export const httpInterceptorProviders = [ 9 | { 10 | provide: HTTP_INTERCEPTORS, 11 | useClass: AuthInterceptor, 12 | multi: true, 13 | }, 14 | { 15 | provide: HTTP_INTERCEPTORS, 16 | useClass: AuthExpiredInterceptor, 17 | multi: true, 18 | }, 19 | { 20 | provide: HTTP_INTERCEPTORS, 21 | useClass: ErrorHandlerInterceptor, 22 | multi: true, 23 | }, 24 | { 25 | provide: HTTP_INTERCEPTORS, 26 | useClass: NotificationInterceptor, 27 | multi: true, 28 | }, 29 | ]; 30 | -------------------------------------------------------------------------------- /src/main/webapp/app/core/interceptor/notification.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse } from '@angular/common/http'; 2 | import { Injectable, inject } from '@angular/core'; 3 | import { Observable } from 'rxjs'; 4 | import { tap } from 'rxjs/operators'; 5 | 6 | import { AlertService } from 'app/core/util/alert.service'; 7 | 8 | @Injectable() 9 | export class NotificationInterceptor implements HttpInterceptor { 10 | private readonly alertService = inject(AlertService); 11 | 12 | intercept(request: HttpRequest, next: HttpHandler): Observable> { 13 | return next.handle(request).pipe( 14 | tap((event: HttpEvent) => { 15 | if (event instanceof HttpResponse) { 16 | let alert: string | null = null; 17 | 18 | for (const headerKey of event.headers.keys()) { 19 | if (headerKey.toLowerCase().endsWith('app-alert')) { 20 | alert = event.headers.get(headerKey); 21 | } 22 | } 23 | 24 | if (alert) { 25 | this.alertService.addAlert({ 26 | type: 'success', 27 | message: alert, 28 | }); 29 | } 30 | } 31 | }), 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/webapp/app/core/request/request-util.ts: -------------------------------------------------------------------------------- 1 | import { HttpParams } from '@angular/common/http'; 2 | 3 | export const createRequestOption = (req?: any): HttpParams => { 4 | let options: HttpParams = new HttpParams(); 5 | 6 | if (req) { 7 | Object.entries(req).forEach(([key, val]) => { 8 | if (val !== undefined && val !== null) { 9 | for (const value of [].concat(req[key]).filter(v => v !== '')) { 10 | options = options.append(key, value); 11 | } 12 | } 13 | }); 14 | } 15 | 16 | return options; 17 | }; 18 | -------------------------------------------------------------------------------- /src/main/webapp/app/core/request/request.model.ts: -------------------------------------------------------------------------------- 1 | export interface Pagination { 2 | page: number; 3 | size: number; 4 | sort: string[]; 5 | } 6 | 7 | export interface Search { 8 | query: string; 9 | } 10 | 11 | export interface SearchWithPagination extends Search, Pagination {} 12 | -------------------------------------------------------------------------------- /src/main/webapp/app/core/util/data-util.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { DataUtils } from './data-util.service'; 4 | 5 | describe('Data Utils Service Test', () => { 6 | let service: DataUtils; 7 | 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({ 10 | providers: [DataUtils], 11 | }); 12 | service = TestBed.inject(DataUtils); 13 | }); 14 | 15 | describe('byteSize', () => { 16 | it('should return the bytesize of the text', () => { 17 | expect(service.byteSize('Hello JHipster')).toBe(`10.5 bytes`); 18 | }); 19 | }); 20 | 21 | describe('openFile', () => { 22 | it('should open the file in the new window', () => { 23 | const newWindow = { ...window }; 24 | window.open = jest.fn(() => newWindow); 25 | window.URL.createObjectURL = jest.fn(); 26 | // 'JHipster' in base64 is 'SkhpcHN0ZXI=' 27 | const data = 'SkhpcHN0ZXI='; 28 | const contentType = 'text/plain'; 29 | service.openFile(data, contentType); 30 | expect(window.open).toHaveBeenCalledTimes(1); 31 | }); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /src/main/webapp/app/core/util/operators.spec.ts: -------------------------------------------------------------------------------- 1 | import { filterNaN, isPresent } from './operators'; 2 | 3 | describe('Operators Test', () => { 4 | describe('isPresent', () => { 5 | it('should remove null and undefined values', () => { 6 | expect([1, null, undefined].filter(isPresent)).toEqual([1]); 7 | }); 8 | }); 9 | 10 | describe('filterNaN', () => { 11 | it('should return 0 for NaN', () => { 12 | expect(filterNaN(NaN)).toBe(0); 13 | }); 14 | it('should return number for a number', () => { 15 | expect(filterNaN(12345)).toBe(12345); 16 | }); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /src/main/webapp/app/core/util/operators.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Function used to workaround https://github.com/microsoft/TypeScript/issues/16069 3 | * es2019 alternative `const filteredArr = myArr.flatMap((x) => x ? x : []);` 4 | */ 5 | export function isPresent(t: T | undefined | null): t is T { 6 | return t !== undefined && t !== null; 7 | } 8 | 9 | export const filterNaN = (input: number): number => (isNaN(input) ? 0 : input); 10 | -------------------------------------------------------------------------------- /src/main/webapp/app/entities/admin/authority/authority.model.ts: -------------------------------------------------------------------------------- 1 | export interface IAuthority { 2 | name: string; 3 | } 4 | 5 | export type NewAuthority = Omit & { name: null }; 6 | -------------------------------------------------------------------------------- /src/main/webapp/app/entities/admin/authority/authority.routes.ts: -------------------------------------------------------------------------------- 1 | import { Routes } from '@angular/router'; 2 | 3 | import { UserRouteAccessService } from 'app/core/auth/user-route-access.service'; 4 | import AuthorityResolve from './route/authority-routing-resolve.service'; 5 | 6 | const authorityRoute: Routes = [ 7 | { 8 | path: '', 9 | loadComponent: () => import('./list/authority.component').then(m => m.AuthorityComponent), 10 | data: { 11 | authorities: ['ROLE_ADMIN'], 12 | }, 13 | canActivate: [UserRouteAccessService], 14 | }, 15 | { 16 | path: ':name/view', 17 | loadComponent: () => import('./detail/authority-detail.component').then(m => m.AuthorityDetailComponent), 18 | resolve: { 19 | authority: AuthorityResolve, 20 | }, 21 | data: { 22 | authorities: ['ROLE_ADMIN'], 23 | }, 24 | canActivate: [UserRouteAccessService], 25 | }, 26 | { 27 | path: 'new', 28 | loadComponent: () => import('./update/authority-update.component').then(m => m.AuthorityUpdateComponent), 29 | resolve: { 30 | authority: AuthorityResolve, 31 | }, 32 | data: { 33 | authorities: ['ROLE_ADMIN'], 34 | }, 35 | canActivate: [UserRouteAccessService], 36 | }, 37 | ]; 38 | 39 | export default authorityRoute; 40 | -------------------------------------------------------------------------------- /src/main/webapp/app/entities/admin/authority/authority.test-samples.ts: -------------------------------------------------------------------------------- 1 | import { IAuthority, NewAuthority } from './authority.model'; 2 | 3 | export const sampleWithRequiredData: IAuthority = { 4 | name: '403e92cf-81b1-401a-9d29-24f9a5b9a4f7', 5 | }; 6 | 7 | export const sampleWithPartialData: IAuthority = { 8 | name: '7bd55631-a2c5-45ca-8628-9d2760e719a6', 9 | }; 10 | 11 | export const sampleWithFullData: IAuthority = { 12 | name: '13e08196-c7f7-4f05-9df5-047fea2afaa7', 13 | }; 14 | 15 | export const sampleWithNewData: NewAuthority = { 16 | name: null, 17 | }; 18 | 19 | Object.freeze(sampleWithNewData); 20 | Object.freeze(sampleWithRequiredData); 21 | Object.freeze(sampleWithPartialData); 22 | Object.freeze(sampleWithFullData); 23 | -------------------------------------------------------------------------------- /src/main/webapp/app/entities/admin/authority/delete/authority-delete-dialog.component.html: -------------------------------------------------------------------------------- 1 | @if (authority) { 2 |
3 | 6 | 7 | 11 | 12 | 21 |
22 | } 23 | -------------------------------------------------------------------------------- /src/main/webapp/app/entities/admin/authority/delete/authority-delete-dialog.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, inject } from '@angular/core'; 2 | import { FormsModule } from '@angular/forms'; 3 | import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; 4 | 5 | import SharedModule from 'app/shared/shared.module'; 6 | import { ITEM_DELETED_EVENT } from 'app/config/navigation.constants'; 7 | import { IAuthority } from '../authority.model'; 8 | import { AuthorityService } from '../service/authority.service'; 9 | 10 | @Component({ 11 | templateUrl: './authority-delete-dialog.component.html', 12 | imports: [SharedModule, FormsModule], 13 | }) 14 | export class AuthorityDeleteDialogComponent { 15 | authority?: IAuthority; 16 | 17 | protected authorityService = inject(AuthorityService); 18 | protected activeModal = inject(NgbActiveModal); 19 | 20 | cancel(): void { 21 | this.activeModal.dismiss(); 22 | } 23 | 24 | confirmDelete(id: string): void { 25 | this.authorityService.delete(id).subscribe(() => { 26 | this.activeModal.close(ITEM_DELETED_EVENT); 27 | }); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/webapp/app/entities/admin/authority/detail/authority-detail.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | @if (authority(); as authorityRef) { 4 |
5 |

Authority

6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 |
14 |
Name
15 |
16 | {{ authorityRef.name }} 17 |
18 |
19 | 20 | 23 |
24 | } 25 |
26 |
27 | -------------------------------------------------------------------------------- /src/main/webapp/app/entities/admin/authority/detail/authority-detail.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, input } from '@angular/core'; 2 | import { RouterModule } from '@angular/router'; 3 | 4 | import SharedModule from 'app/shared/shared.module'; 5 | import { IAuthority } from '../authority.model'; 6 | 7 | @Component({ 8 | selector: 'jhi-authority-detail', 9 | templateUrl: './authority-detail.component.html', 10 | imports: [SharedModule, RouterModule], 11 | }) 12 | export class AuthorityDetailComponent { 13 | authority = input(null); 14 | 15 | previousState(): void { 16 | window.history.back(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/webapp/app/entities/admin/authority/route/authority-routing-resolve.service.ts: -------------------------------------------------------------------------------- 1 | import { inject } from '@angular/core'; 2 | import { HttpResponse } from '@angular/common/http'; 3 | import { ActivatedRouteSnapshot, Router } from '@angular/router'; 4 | import { EMPTY, Observable, of } from 'rxjs'; 5 | import { mergeMap } from 'rxjs/operators'; 6 | 7 | import { IAuthority } from '../authority.model'; 8 | import { AuthorityService } from '../service/authority.service'; 9 | 10 | const authorityResolve = (route: ActivatedRouteSnapshot): Observable => { 11 | const id = route.params.name; 12 | if (id) { 13 | return inject(AuthorityService) 14 | .find(id) 15 | .pipe( 16 | mergeMap((authority: HttpResponse) => { 17 | if (authority.body) { 18 | return of(authority.body); 19 | } 20 | inject(Router).navigate(['404']); 21 | return EMPTY; 22 | }), 23 | ); 24 | } 25 | return of(null); 26 | }; 27 | 28 | export default authorityResolve; 29 | -------------------------------------------------------------------------------- /src/main/webapp/app/entities/entity-navbar-items.ts: -------------------------------------------------------------------------------- 1 | import NavbarItem from 'app/layouts/navbar/navbar-item.model'; 2 | 3 | export const EntityNavbarItems: NavbarItem[] = [ 4 | { 5 | name: 'BankAccount', 6 | route: '/bank-account', 7 | }, 8 | ]; 9 | -------------------------------------------------------------------------------- /src/main/webapp/app/entities/entity.routes.ts: -------------------------------------------------------------------------------- 1 | import { Routes } from '@angular/router'; 2 | 3 | const routes: Routes = [ 4 | { 5 | path: 'authority', 6 | data: { pageTitle: 'Authorities' }, 7 | loadChildren: () => import('./admin/authority/authority.routes'), 8 | }, 9 | { 10 | path: 'bank-account', 11 | data: { pageTitle: 'BankAccounts' }, 12 | loadChildren: () => import('./jhipsterSampleMicroservice/bank-account/bank-account.routes'), 13 | }, 14 | /* jhipster-needle-add-entity-route - JHipster will add entity modules routes here */ 15 | ]; 16 | 17 | export default routes; 18 | -------------------------------------------------------------------------------- /src/main/webapp/app/entities/jhipsterSampleMicroservice/bank-account/bank-account.model.ts: -------------------------------------------------------------------------------- 1 | export interface IBankAccount { 2 | id: number; 3 | name?: string | null; 4 | balance?: number | null; 5 | } 6 | 7 | export type NewBankAccount = Omit & { id: null }; 8 | -------------------------------------------------------------------------------- /src/main/webapp/app/entities/jhipsterSampleMicroservice/bank-account/bank-account.routes.ts: -------------------------------------------------------------------------------- 1 | import { Routes } from '@angular/router'; 2 | 3 | import { UserRouteAccessService } from 'app/core/auth/user-route-access.service'; 4 | import { ASC } from 'app/config/navigation.constants'; 5 | import BankAccountResolve from './route/bank-account-routing-resolve.service'; 6 | 7 | const bankAccountRoute: Routes = [ 8 | { 9 | path: '', 10 | loadComponent: () => import('./list/bank-account.component').then(m => m.BankAccountComponent), 11 | data: { 12 | defaultSort: `id,${ASC}`, 13 | }, 14 | canActivate: [UserRouteAccessService], 15 | }, 16 | { 17 | path: ':id/view', 18 | loadComponent: () => import('./detail/bank-account-detail.component').then(m => m.BankAccountDetailComponent), 19 | resolve: { 20 | bankAccount: BankAccountResolve, 21 | }, 22 | canActivate: [UserRouteAccessService], 23 | }, 24 | { 25 | path: 'new', 26 | loadComponent: () => import('./update/bank-account-update.component').then(m => m.BankAccountUpdateComponent), 27 | resolve: { 28 | bankAccount: BankAccountResolve, 29 | }, 30 | canActivate: [UserRouteAccessService], 31 | }, 32 | { 33 | path: ':id/edit', 34 | loadComponent: () => import('./update/bank-account-update.component').then(m => m.BankAccountUpdateComponent), 35 | resolve: { 36 | bankAccount: BankAccountResolve, 37 | }, 38 | canActivate: [UserRouteAccessService], 39 | }, 40 | ]; 41 | 42 | export default bankAccountRoute; 43 | -------------------------------------------------------------------------------- /src/main/webapp/app/entities/jhipsterSampleMicroservice/bank-account/bank-account.test-samples.ts: -------------------------------------------------------------------------------- 1 | import { IBankAccount, NewBankAccount } from './bank-account.model'; 2 | 3 | export const sampleWithRequiredData: IBankAccount = { 4 | id: 17167, 5 | name: 'um via', 6 | balance: 2293.59, 7 | }; 8 | 9 | export const sampleWithPartialData: IBankAccount = { 10 | id: 18934, 11 | name: 'bright whenever', 12 | balance: 7939.53, 13 | }; 14 | 15 | export const sampleWithFullData: IBankAccount = { 16 | id: 13483, 17 | name: 'gigantic', 18 | balance: 26580.31, 19 | }; 20 | 21 | export const sampleWithNewData: NewBankAccount = { 22 | name: 'clonk porter', 23 | balance: 26302.97, 24 | id: null, 25 | }; 26 | 27 | Object.freeze(sampleWithNewData); 28 | Object.freeze(sampleWithRequiredData); 29 | Object.freeze(sampleWithPartialData); 30 | Object.freeze(sampleWithFullData); 31 | -------------------------------------------------------------------------------- /src/main/webapp/app/entities/jhipsterSampleMicroservice/bank-account/delete/bank-account-delete-dialog.component.html: -------------------------------------------------------------------------------- 1 | @if (bankAccount) { 2 |
3 | 6 | 7 | 11 | 12 | 21 |
22 | } 23 | -------------------------------------------------------------------------------- /src/main/webapp/app/entities/jhipsterSampleMicroservice/bank-account/delete/bank-account-delete-dialog.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, inject } from '@angular/core'; 2 | import { FormsModule } from '@angular/forms'; 3 | import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; 4 | 5 | import SharedModule from 'app/shared/shared.module'; 6 | import { ITEM_DELETED_EVENT } from 'app/config/navigation.constants'; 7 | import { IBankAccount } from '../bank-account.model'; 8 | import { BankAccountService } from '../service/bank-account.service'; 9 | 10 | @Component({ 11 | templateUrl: './bank-account-delete-dialog.component.html', 12 | imports: [SharedModule, FormsModule], 13 | }) 14 | export class BankAccountDeleteDialogComponent { 15 | bankAccount?: IBankAccount; 16 | 17 | protected bankAccountService = inject(BankAccountService); 18 | protected activeModal = inject(NgbActiveModal); 19 | 20 | cancel(): void { 21 | this.activeModal.dismiss(); 22 | } 23 | 24 | confirmDelete(id: number): void { 25 | this.bankAccountService.delete(id).subscribe(() => { 26 | this.activeModal.close(ITEM_DELETED_EVENT); 27 | }); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/webapp/app/entities/jhipsterSampleMicroservice/bank-account/detail/bank-account-detail.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | @if (bankAccount(); as bankAccountRef) { 4 |
5 |

Bank Account

6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 |
14 |
ID
15 |
16 | {{ bankAccountRef.id }} 17 |
18 |
Name
19 |
20 | {{ bankAccountRef.name }} 21 |
22 |
Balance
23 |
24 | {{ bankAccountRef.balance }} 25 |
26 |
27 | 28 | 31 | 32 | 35 |
36 | } 37 |
38 |
39 | -------------------------------------------------------------------------------- /src/main/webapp/app/entities/jhipsterSampleMicroservice/bank-account/detail/bank-account-detail.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, input } from '@angular/core'; 2 | import { RouterModule } from '@angular/router'; 3 | 4 | import SharedModule from 'app/shared/shared.module'; 5 | import { IBankAccount } from '../bank-account.model'; 6 | 7 | @Component({ 8 | selector: 'jhi-bank-account-detail', 9 | templateUrl: './bank-account-detail.component.html', 10 | imports: [SharedModule, RouterModule], 11 | }) 12 | export class BankAccountDetailComponent { 13 | bankAccount = input(null); 14 | 15 | previousState(): void { 16 | window.history.back(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/webapp/app/entities/jhipsterSampleMicroservice/bank-account/route/bank-account-routing-resolve.service.ts: -------------------------------------------------------------------------------- 1 | import { inject } from '@angular/core'; 2 | import { HttpResponse } from '@angular/common/http'; 3 | import { ActivatedRouteSnapshot, Router } from '@angular/router'; 4 | import { EMPTY, Observable, of } from 'rxjs'; 5 | import { mergeMap } from 'rxjs/operators'; 6 | 7 | import { IBankAccount } from '../bank-account.model'; 8 | import { BankAccountService } from '../service/bank-account.service'; 9 | 10 | const bankAccountResolve = (route: ActivatedRouteSnapshot): Observable => { 11 | const id = route.params.id; 12 | if (id) { 13 | return inject(BankAccountService) 14 | .find(id) 15 | .pipe( 16 | mergeMap((bankAccount: HttpResponse) => { 17 | if (bankAccount.body) { 18 | return of(bankAccount.body); 19 | } 20 | inject(Router).navigate(['404']); 21 | return EMPTY; 22 | }), 23 | ); 24 | } 25 | return of(null); 26 | }; 27 | 28 | export default bankAccountResolve; 29 | -------------------------------------------------------------------------------- /src/main/webapp/app/entities/user/user.model.ts: -------------------------------------------------------------------------------- 1 | export interface IUser { 2 | id: number; 3 | login?: string | null; 4 | } 5 | -------------------------------------------------------------------------------- /src/main/webapp/app/entities/user/user.test-samples.ts: -------------------------------------------------------------------------------- 1 | import { IUser } from './user.model'; 2 | 3 | export const sampleWithRequiredData: IUser = { 4 | id: 24814, 5 | login: 'nuGud', 6 | }; 7 | 8 | export const sampleWithPartialData: IUser = { 9 | id: 966, 10 | login: 'a', 11 | }; 12 | 13 | export const sampleWithFullData: IUser = { 14 | id: 5440, 15 | login: 'h', 16 | }; 17 | Object.freeze(sampleWithRequiredData); 18 | Object.freeze(sampleWithPartialData); 19 | Object.freeze(sampleWithFullData); 20 | -------------------------------------------------------------------------------- /src/main/webapp/app/home/home.component.scss: -------------------------------------------------------------------------------- 1 | /* ========================================================================== 2 | Main page styles 3 | ========================================================================== */ 4 | 5 | .hipster { 6 | display: inline-block; 7 | width: 347px; 8 | height: 497px; 9 | background: url('../../content/images/jhipster_family_member_1.svg') no-repeat center top; 10 | background-size: contain; 11 | } 12 | 13 | /* wait autoprefixer update to allow simple generation of high pixel density media query */ 14 | @media only screen and (-webkit-min-device-pixel-ratio: 2), 15 | only screen and (-moz-min-device-pixel-ratio: 2), 16 | only screen and (-o-min-device-pixel-ratio: 2/1), 17 | only screen and (min-resolution: 192dpi), 18 | only screen and (min-resolution: 2dppx) { 19 | .hipster { 20 | background: url('../../content/images/jhipster_family_member_1.svg') no-repeat center top; 21 | background-size: contain; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/webapp/app/home/home.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnDestroy, OnInit, inject, signal } from '@angular/core'; 2 | import { Router, RouterModule } from '@angular/router'; 3 | import { Subject } from 'rxjs'; 4 | import { takeUntil } from 'rxjs/operators'; 5 | 6 | import SharedModule from 'app/shared/shared.module'; 7 | import { AccountService } from 'app/core/auth/account.service'; 8 | import { Account } from 'app/core/auth/account.model'; 9 | 10 | @Component({ 11 | selector: 'jhi-home', 12 | templateUrl: './home.component.html', 13 | styleUrl: './home.component.scss', 14 | imports: [SharedModule, RouterModule], 15 | }) 16 | export default class HomeComponent implements OnInit, OnDestroy { 17 | account = signal(null); 18 | 19 | private readonly destroy$ = new Subject(); 20 | 21 | private readonly accountService = inject(AccountService); 22 | private readonly router = inject(Router); 23 | 24 | ngOnInit(): void { 25 | this.accountService 26 | .getAuthenticationState() 27 | .pipe(takeUntil(this.destroy$)) 28 | .subscribe(account => this.account.set(account)); 29 | } 30 | 31 | login(): void { 32 | this.router.navigate(['/login']); 33 | } 34 | 35 | ngOnDestroy(): void { 36 | this.destroy$.next(); 37 | this.destroy$.complete(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/webapp/app/layouts/error/error.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 |
6 | 7 |
8 |

Error page!

9 | 10 | @if (errorMessage(); as errMessage) { 11 |
{{ errMessage }}
12 | } 13 |
14 |
15 |
16 | -------------------------------------------------------------------------------- /src/main/webapp/app/layouts/error/error.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, inject, signal } from '@angular/core'; 2 | import { ActivatedRoute } from '@angular/router'; 3 | import SharedModule from 'app/shared/shared.module'; 4 | 5 | @Component({ 6 | selector: 'jhi-error', 7 | templateUrl: './error.component.html', 8 | imports: [SharedModule], 9 | }) 10 | export default class ErrorComponent implements OnInit { 11 | errorMessage = signal(undefined); 12 | 13 | private readonly route = inject(ActivatedRoute); 14 | 15 | ngOnInit(): void { 16 | this.route.data.subscribe(routeData => { 17 | if (routeData.errorMessage) { 18 | this.errorMessage.set(routeData.errorMessage); 19 | } 20 | }); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/webapp/app/layouts/error/error.route.ts: -------------------------------------------------------------------------------- 1 | import { Routes } from '@angular/router'; 2 | 3 | export const errorRoute: Routes = [ 4 | { 5 | path: 'error', 6 | loadComponent: () => import('./error.component'), 7 | title: 'Error page!', 8 | }, 9 | { 10 | path: 'accessdenied', 11 | loadComponent: () => import('./error.component'), 12 | data: { 13 | errorMessage: 'You are not authorized to access this page.', 14 | }, 15 | title: 'Error page!', 16 | }, 17 | { 18 | path: '404', 19 | loadComponent: () => import('./error.component'), 20 | data: { 21 | errorMessage: 'The page does not exist.', 22 | }, 23 | title: 'Error page!', 24 | }, 25 | { 26 | path: '**', 27 | redirectTo: '/404', 28 | }, 29 | ]; 30 | -------------------------------------------------------------------------------- /src/main/webapp/app/layouts/footer/footer.component.html: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/main/webapp/app/layouts/footer/footer.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'jhi-footer', 5 | templateUrl: './footer.component.html', 6 | }) 7 | export default class FooterComponent {} 8 | -------------------------------------------------------------------------------- /src/main/webapp/app/layouts/main/main.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |
6 | 7 |
8 |
9 | 10 |
11 | 12 | 13 |
14 | -------------------------------------------------------------------------------- /src/main/webapp/app/layouts/main/main.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, inject } from '@angular/core'; 2 | import { Router, RouterOutlet } from '@angular/router'; 3 | 4 | import { AccountService } from 'app/core/auth/account.service'; 5 | import { AppPageTitleStrategy } from 'app/app-page-title-strategy'; 6 | import FooterComponent from '../footer/footer.component'; 7 | import PageRibbonComponent from '../profiles/page-ribbon.component'; 8 | 9 | @Component({ 10 | selector: 'jhi-main', 11 | templateUrl: './main.component.html', 12 | providers: [AppPageTitleStrategy], 13 | imports: [RouterOutlet, FooterComponent, PageRibbonComponent], 14 | }) 15 | export default class MainComponent implements OnInit { 16 | private readonly router = inject(Router); 17 | private readonly appPageTitleStrategy = inject(AppPageTitleStrategy); 18 | private readonly accountService = inject(AccountService); 19 | 20 | ngOnInit(): void { 21 | // try to log in automatically 22 | this.accountService.identity().subscribe(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/webapp/app/layouts/navbar/navbar-item.model.d.ts: -------------------------------------------------------------------------------- 1 | type NavbarItem = { 2 | name: string; 3 | route: string; 4 | }; 5 | 6 | export default NavbarItem; 7 | -------------------------------------------------------------------------------- /src/main/webapp/app/layouts/navbar/navbar.component.scss: -------------------------------------------------------------------------------- 1 | @import 'bootstrap/scss/functions'; 2 | @import 'bootstrap/scss/variables'; 3 | 4 | /* ========================================================================== 5 | Navbar 6 | ========================================================================== */ 7 | 8 | .navbar-version { 9 | font-size: 0.65em; 10 | color: $navbar-dark-color; 11 | } 12 | 13 | .profile-image { 14 | height: 1.75em; 15 | width: 1.75em; 16 | } 17 | 18 | .navbar { 19 | padding: 0.2rem 1rem; 20 | 21 | a.nav-link { 22 | font-weight: 400; 23 | } 24 | } 25 | 26 | /* ========================================================================== 27 | Logo styles 28 | ========================================================================== */ 29 | .logo-img { 30 | height: 45px; 31 | width: 45px; 32 | display: inline-block; 33 | vertical-align: middle; 34 | background: url('/content/images/logo-jhipster.png') no-repeat center center; 35 | background-size: contain; 36 | } 37 | -------------------------------------------------------------------------------- /src/main/webapp/app/layouts/profiles/page-ribbon.component.scss: -------------------------------------------------------------------------------- 1 | /* ========================================================================== 2 | Development Ribbon 3 | ========================================================================== */ 4 | .ribbon { 5 | background-color: rgba(170, 0, 0, 0.5); 6 | overflow: hidden; 7 | position: absolute; 8 | top: 40px; 9 | white-space: nowrap; 10 | width: 15em; 11 | z-index: 9999; 12 | pointer-events: none; 13 | opacity: 0.75; 14 | a { 15 | color: #fff; 16 | display: block; 17 | font-weight: 400; 18 | margin: 1px 0; 19 | padding: 10px 50px; 20 | text-align: center; 21 | text-decoration: none; 22 | text-shadow: 0 0 5px #444; 23 | pointer-events: none; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/webapp/app/layouts/profiles/page-ribbon.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; 2 | import { provideHttpClient } from '@angular/common/http'; 3 | import { of } from 'rxjs'; 4 | 5 | import { ProfileInfo } from 'app/layouts/profiles/profile-info.model'; 6 | import { ProfileService } from 'app/layouts/profiles/profile.service'; 7 | 8 | import PageRibbonComponent from './page-ribbon.component'; 9 | 10 | describe('Page Ribbon Component', () => { 11 | let comp: PageRibbonComponent; 12 | let fixture: ComponentFixture; 13 | let profileService: ProfileService; 14 | 15 | beforeEach(waitForAsync(() => { 16 | TestBed.configureTestingModule({ 17 | imports: [PageRibbonComponent], 18 | providers: [provideHttpClient()], 19 | }) 20 | .overrideTemplate(PageRibbonComponent, '') 21 | .compileComponents(); 22 | })); 23 | 24 | beforeEach(() => { 25 | fixture = TestBed.createComponent(PageRibbonComponent); 26 | comp = fixture.componentInstance; 27 | profileService = TestBed.inject(ProfileService); 28 | }); 29 | 30 | it('should call profileService.getProfileInfo on init', () => { 31 | // GIVEN 32 | jest.spyOn(profileService, 'getProfileInfo').mockReturnValue(of(new ProfileInfo())); 33 | 34 | // WHEN 35 | comp.ngOnInit(); 36 | 37 | // THEN 38 | expect(profileService.getProfileInfo).toHaveBeenCalled(); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /src/main/webapp/app/layouts/profiles/page-ribbon.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Injector, OnInit, Signal, inject } from '@angular/core'; 2 | import { Observable } from 'rxjs'; 3 | import { map } from 'rxjs/operators'; 4 | import { toSignal } from '@angular/core/rxjs-interop'; 5 | 6 | import SharedModule from 'app/shared/shared.module'; 7 | import { ProfileService } from './profile.service'; 8 | 9 | @Component({ 10 | selector: 'jhi-page-ribbon', 11 | template: ` 12 | @if (ribbonEnvSignal; as ribbonEnv) { 13 | 16 | } 17 | `, 18 | styleUrl: './page-ribbon.component.scss', 19 | imports: [SharedModule], 20 | }) 21 | export default class PageRibbonComponent implements OnInit { 22 | ribbonEnvSignal?: Signal; 23 | private readonly injector = inject(Injector); 24 | private readonly profileService = inject(ProfileService); 25 | 26 | ngOnInit(): void { 27 | const ribbonEnv$: Observable = this.profileService.getProfileInfo().pipe(map(profileInfo => profileInfo.ribbonEnv)); 28 | this.ribbonEnvSignal = toSignal(ribbonEnv$, { injector: this.injector }); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/webapp/app/layouts/profiles/profile-info.model.ts: -------------------------------------------------------------------------------- 1 | export interface InfoResponse { 2 | 'display-ribbon-on-profiles'?: string; 3 | git?: any; 4 | build?: any; 5 | activeProfiles?: string[]; 6 | } 7 | 8 | export class ProfileInfo { 9 | constructor( 10 | public activeProfiles?: string[], 11 | public ribbonEnv?: string, 12 | public inProduction?: boolean, 13 | public openAPIEnabled?: boolean, 14 | ) {} 15 | } 16 | -------------------------------------------------------------------------------- /src/main/webapp/app/login/login.model.ts: -------------------------------------------------------------------------------- 1 | export class Login { 2 | constructor( 3 | public username: string, 4 | public password: string, 5 | public rememberMe: boolean, 6 | ) {} 7 | } 8 | -------------------------------------------------------------------------------- /src/main/webapp/app/login/login.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, inject } from '@angular/core'; 2 | import { Observable } from 'rxjs'; 3 | import { mergeMap } from 'rxjs/operators'; 4 | 5 | import { Account } from 'app/core/auth/account.model'; 6 | import { AccountService } from 'app/core/auth/account.service'; 7 | import { AuthServerProvider } from 'app/core/auth/auth-jwt.service'; 8 | import { Login } from './login.model'; 9 | 10 | @Injectable({ providedIn: 'root' }) 11 | export class LoginService { 12 | private readonly accountService = inject(AccountService); 13 | private readonly authServerProvider = inject(AuthServerProvider); 14 | 15 | login(credentials: Login): Observable { 16 | return this.authServerProvider.login(credentials).pipe(mergeMap(() => this.accountService.identity(true))); 17 | } 18 | 19 | logout(): void { 20 | this.authServerProvider.logout().subscribe({ complete: () => this.accountService.authenticate(null) }); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/webapp/app/shared/alert/alert-error.component.html: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /src/main/webapp/app/shared/alert/alert-error.model.ts: -------------------------------------------------------------------------------- 1 | export class AlertError { 2 | constructor(public message: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /src/main/webapp/app/shared/alert/alert.component.html: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /src/main/webapp/app/shared/alert/alert.component.spec.ts: -------------------------------------------------------------------------------- 1 | jest.mock('app/core/util/alert.service'); 2 | 3 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; 4 | 5 | import { AlertService } from 'app/core/util/alert.service'; 6 | 7 | import { AlertComponent } from './alert.component'; 8 | 9 | describe('Alert Component', () => { 10 | let comp: AlertComponent; 11 | let fixture: ComponentFixture; 12 | let mockAlertService: AlertService; 13 | 14 | beforeEach(waitForAsync(() => { 15 | TestBed.configureTestingModule({ 16 | imports: [AlertComponent], 17 | providers: [AlertService], 18 | }) 19 | .overrideTemplate(AlertComponent, '') 20 | .compileComponents(); 21 | })); 22 | 23 | beforeEach(() => { 24 | fixture = TestBed.createComponent(AlertComponent); 25 | comp = fixture.componentInstance; 26 | mockAlertService = TestBed.inject(AlertService); 27 | }); 28 | 29 | it('should call alertService.get on init', () => { 30 | // WHEN 31 | comp.ngOnInit(); 32 | 33 | // THEN 34 | expect(mockAlertService.get).toHaveBeenCalled(); 35 | }); 36 | 37 | it('should call alertService.clear on destroy', () => { 38 | // WHEN 39 | comp.ngOnDestroy(); 40 | 41 | // THEN 42 | expect(mockAlertService.clear).toHaveBeenCalled(); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /src/main/webapp/app/shared/alert/alert.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnDestroy, OnInit, inject, signal } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; 4 | 5 | import { Alert, AlertService } from 'app/core/util/alert.service'; 6 | 7 | @Component({ 8 | selector: 'jhi-alert', 9 | templateUrl: './alert.component.html', 10 | imports: [CommonModule, NgbModule], 11 | }) 12 | export class AlertComponent implements OnInit, OnDestroy { 13 | alerts = signal([]); 14 | 15 | private readonly alertService = inject(AlertService); 16 | 17 | ngOnInit(): void { 18 | this.alerts.set(this.alertService.get()); 19 | } 20 | 21 | setClasses(alert: Alert): Record { 22 | const classes = { 'jhi-toast': Boolean(alert.toast) }; 23 | if (alert.position) { 24 | return { ...classes, [alert.position]: true }; 25 | } 26 | return classes; 27 | } 28 | 29 | ngOnDestroy(): void { 30 | this.alertService.clear(); 31 | } 32 | 33 | close(alert: Alert): void { 34 | alert.close?.(this.alerts()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/webapp/app/shared/auth/has-any-authority.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, TemplateRef, ViewContainerRef, computed, effect, inject, input } from '@angular/core'; 2 | 3 | import { AccountService } from 'app/core/auth/account.service'; 4 | 5 | /** 6 | * @whatItDoes Conditionally includes an HTML element if current user has any 7 | * of the authorities passed as the `expression`. 8 | * 9 | * @howToUse 10 | * ``` 11 | * ... 12 | * 13 | * ... 14 | * ``` 15 | */ 16 | @Directive({ 17 | selector: '[jhiHasAnyAuthority]', 18 | }) 19 | export default class HasAnyAuthorityDirective { 20 | public authorities = input([], { alias: 'jhiHasAnyAuthority' }); 21 | 22 | private readonly templateRef = inject(TemplateRef); 23 | private readonly viewContainerRef = inject(ViewContainerRef); 24 | 25 | constructor() { 26 | const accountService = inject(AccountService); 27 | const currentAccount = accountService.trackCurrentAccount(); 28 | const hasPermission = computed(() => currentAccount()?.authorities && accountService.hasAnyAuthority(this.authorities())); 29 | 30 | effect(() => { 31 | if (hasPermission()) { 32 | this.viewContainerRef.createEmbeddedView(this.templateRef); 33 | } else { 34 | this.viewContainerRef.clear(); 35 | } 36 | }); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/webapp/app/shared/date/duration.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | import dayjs from 'dayjs/esm'; 4 | 5 | @Pipe({ 6 | name: 'duration', 7 | }) 8 | export default class DurationPipe implements PipeTransform { 9 | transform(value: any): string { 10 | if (value) { 11 | return dayjs.duration(value).humanize(); 12 | } 13 | return ''; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/webapp/app/shared/date/format-medium-date.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import dayjs from 'dayjs/esm'; 2 | 3 | import FormatMediumDatePipe from './format-medium-date.pipe'; 4 | 5 | describe('FormatMediumDatePipe', () => { 6 | const formatMediumDatePipe = new FormatMediumDatePipe(); 7 | 8 | it('should return an empty string when receive undefined', () => { 9 | expect(formatMediumDatePipe.transform(undefined)).toBe(''); 10 | }); 11 | 12 | it('should return an empty string when receive null', () => { 13 | expect(formatMediumDatePipe.transform(null)).toBe(''); 14 | }); 15 | 16 | it('should format date like this D MMM YYYY', () => { 17 | expect(formatMediumDatePipe.transform(dayjs('2020-11-16').locale('fr'))).toBe('16 Nov 2020'); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /src/main/webapp/app/shared/date/format-medium-date.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | import dayjs from 'dayjs/esm'; 4 | 5 | @Pipe({ 6 | name: 'formatMediumDate', 7 | }) 8 | export default class FormatMediumDatePipe implements PipeTransform { 9 | transform(day: dayjs.Dayjs | null | undefined): string { 10 | return day ? day.format('D MMM YYYY') : ''; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/webapp/app/shared/date/format-medium-datetime.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import dayjs from 'dayjs/esm'; 2 | 3 | import FormatMediumDatetimePipe from './format-medium-datetime.pipe'; 4 | 5 | describe('FormatMediumDatePipe', () => { 6 | const formatMediumDatetimePipe = new FormatMediumDatetimePipe(); 7 | 8 | it('should return an empty string when receive undefined', () => { 9 | expect(formatMediumDatetimePipe.transform(undefined)).toBe(''); 10 | }); 11 | 12 | it('should return an empty string when receive null', () => { 13 | expect(formatMediumDatetimePipe.transform(null)).toBe(''); 14 | }); 15 | 16 | it('should format date like this D MMM YYYY', () => { 17 | expect(formatMediumDatetimePipe.transform(dayjs('2020-11-16').locale('fr'))).toBe('16 Nov 2020 00:00:00'); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /src/main/webapp/app/shared/date/format-medium-datetime.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | import dayjs from 'dayjs/esm'; 4 | 5 | @Pipe({ 6 | name: 'formatMediumDatetime', 7 | }) 8 | export default class FormatMediumDatetimePipe implements PipeTransform { 9 | transform(day: dayjs.Dayjs | null | undefined): string { 10 | return day ? day.format('D MMM YYYY HH:mm:ss') : ''; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/webapp/app/shared/date/index.ts: -------------------------------------------------------------------------------- 1 | export { default as DurationPipe } from './duration.pipe'; 2 | export { default as FormatMediumDatePipe } from './format-medium-date.pipe'; 3 | export { default as FormatMediumDatetimePipe } from './format-medium-datetime.pipe'; 4 | -------------------------------------------------------------------------------- /src/main/webapp/app/shared/filter/filter.component.html: -------------------------------------------------------------------------------- 1 | @if (filters().hasAnyFilterSet()) { 2 |
3 | Following filters are set 4 | 7 |
    8 | @for (filterOption of filters().filterOptions; track filterOption.name) { 9 | @for (value of filterOption.values; track value) { 10 |
  • 11 | {{ filterOption.name }}: {{ value }} 12 | 15 |
  • 16 | } 17 | } 18 |
19 |
20 | } 21 | -------------------------------------------------------------------------------- /src/main/webapp/app/shared/filter/filter.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, input } from '@angular/core'; 2 | import SharedModule from '../shared.module'; 3 | import { IFilterOptions } from './filter.model'; 4 | 5 | @Component({ 6 | selector: 'jhi-filter', 7 | imports: [SharedModule], 8 | templateUrl: './filter.component.html', 9 | }) 10 | export default class FilterComponent { 11 | readonly filters = input.required(); 12 | 13 | clearAllFilters(): void { 14 | this.filters().clear(); 15 | } 16 | 17 | clearFilter(filterName: string, value: string): void { 18 | this.filters().removeFilter(filterName, value); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/webapp/app/shared/filter/index.ts: -------------------------------------------------------------------------------- 1 | export { default as FilterComponent } from './filter.component'; 2 | export * from './filter.model'; 3 | -------------------------------------------------------------------------------- /src/main/webapp/app/shared/pagination/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ItemCountComponent } from './item-count.component'; 2 | -------------------------------------------------------------------------------- /src/main/webapp/app/shared/pagination/item-count.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, computed, input } from '@angular/core'; 2 | 3 | /** 4 | * A component that will take care of item count statistics of a pagination. 5 | */ 6 | @Component({ 7 | selector: 'jhi-item-count', 8 | template: `
Showing {{ first() }} - {{ second() }} of {{ total() }} items.
`, 9 | }) 10 | export default class ItemCountComponent { 11 | /** 12 | * @param params Contains parameters for component: 13 | * page Current page number 14 | * totalItems Total number of items 15 | * itemsPerPage Number of items per page 16 | */ 17 | params = input<{ 18 | page?: number; 19 | totalItems?: number; 20 | itemsPerPage?: number; 21 | }>(); 22 | 23 | first = computed(() => { 24 | const params = this.params(); 25 | if (params?.page && params.totalItems !== undefined && params.itemsPerPage) { 26 | return (params.page - 1) * params.itemsPerPage + 1; 27 | } 28 | return undefined; 29 | }); 30 | 31 | second = computed(() => { 32 | const params = this.params(); 33 | if (params?.page && params.totalItems !== undefined && params.itemsPerPage) { 34 | return params.page * params.itemsPerPage < params.totalItems ? params.page * params.itemsPerPage : params.totalItems; 35 | } 36 | return undefined; 37 | }); 38 | 39 | total = computed(() => this.params()?.totalItems); 40 | } 41 | -------------------------------------------------------------------------------- /src/main/webapp/app/shared/shared.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; 4 | import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; 5 | import { AlertComponent } from './alert/alert.component'; 6 | import { AlertErrorComponent } from './alert/alert-error.component'; 7 | 8 | /** 9 | * Application wide Module 10 | */ 11 | @NgModule({ 12 | imports: [AlertComponent, AlertErrorComponent], 13 | exports: [CommonModule, NgbModule, FontAwesomeModule, AlertComponent, AlertErrorComponent], 14 | }) 15 | export default class SharedModule {} 16 | -------------------------------------------------------------------------------- /src/main/webapp/app/shared/sort/index.ts: -------------------------------------------------------------------------------- 1 | export * from './sort-by.directive'; 2 | export * from './sort-state'; 3 | export * from './sort.directive'; 4 | export * from './sort.service'; 5 | -------------------------------------------------------------------------------- /src/main/webapp/app/shared/sort/sort-by.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, HostListener, contentChild, effect, inject, input } from '@angular/core'; 2 | import { FaIconComponent } from '@fortawesome/angular-fontawesome'; 3 | import { IconDefinition, faSort, faSortDown, faSortUp } from '@fortawesome/free-solid-svg-icons'; 4 | 5 | import { SortDirective } from './sort.directive'; 6 | 7 | @Directive({ 8 | selector: '[jhiSortBy]', 9 | }) 10 | export class SortByDirective { 11 | readonly jhiSortBy = input.required(); 12 | 13 | iconComponent = contentChild(FaIconComponent); 14 | 15 | protected sortIcon = faSort; 16 | protected sortAscIcon = faSortUp; 17 | protected sortDescIcon = faSortDown; 18 | 19 | private readonly sort = inject(SortDirective, { host: true }); 20 | 21 | constructor() { 22 | effect(() => { 23 | if (this.iconComponent()) { 24 | let icon: IconDefinition = this.sortIcon; 25 | const { predicate, order } = this.sort.sortState(); 26 | if (predicate === this.jhiSortBy() && order !== undefined) { 27 | icon = order === 'asc' ? this.sortAscIcon : this.sortDescIcon; 28 | } 29 | this.iconComponent()!.icon = icon.iconName; 30 | this.iconComponent()!.render(); 31 | } 32 | }); 33 | } 34 | 35 | @HostListener('click') 36 | onClick(): void { 37 | if (this.iconComponent()) { 38 | this.sort.sort(this.jhiSortBy()); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/webapp/app/shared/sort/sort-state.ts: -------------------------------------------------------------------------------- 1 | import { WritableSignal, signal } from '@angular/core'; 2 | 3 | export type SortOrder = 'asc' | 'desc'; 4 | 5 | export type SortState = { predicate?: string; order?: SortOrder }; 6 | 7 | export const sortStateSignal = (state: SortState): WritableSignal => 8 | signal(state, { 9 | equal: (a, b) => a.predicate === b.predicate && a.order === b.order, 10 | }); 11 | -------------------------------------------------------------------------------- /src/main/webapp/app/shared/sort/sort.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, model, output } from '@angular/core'; 2 | import { SortOrder, SortState } from './sort-state'; 3 | 4 | @Directive({ 5 | selector: '[jhiSort]', 6 | }) 7 | export class SortDirective { 8 | readonly sortState = model.required(); 9 | 10 | readonly sortChange = output(); 11 | 12 | sort(field: string): void { 13 | const { predicate, order } = this.sortState(); 14 | const toggle = (): SortOrder => (order === 'asc' ? 'desc' : 'asc'); 15 | const newSortState = { predicate: field, order: field !== predicate ? 'asc' : toggle() }; 16 | this.sortState.update(() => newSortState); 17 | this.sortChange.emit(newSortState); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/webapp/app/shared/sort/sort.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { SortState } from './sort-state'; 3 | 4 | @Injectable({ providedIn: 'root' }) 5 | export class SortService { 6 | private readonly collator = new Intl.Collator(undefined, { 7 | numeric: true, 8 | sensitivity: 'base', 9 | }); 10 | 11 | startSort({ predicate, order }: Required, fallback?: Required): (a: any, b: any) => number { 12 | const multiply = order === 'desc' ? -1 : 1; 13 | return (a: any, b: any) => { 14 | const compare = this.collator.compare(a[predicate], b[predicate]); 15 | if (compare === 0 && fallback) { 16 | return this.startSort(fallback)(a, b); 17 | } 18 | return compare * multiply; 19 | }; 20 | } 21 | 22 | parseSortParam(sortParam: string | undefined): SortState { 23 | if (sortParam?.includes(',')) { 24 | const split = sortParam.split(','); 25 | if (split[0]) { 26 | return { predicate: split[0], order: split[1] as any }; 27 | } 28 | } 29 | return { predicate: sortParam?.length ? sortParam : undefined }; 30 | } 31 | 32 | buildSortParam({ predicate, order }: SortState, fallback?: string): string[] { 33 | const sortParam = predicate && order ? [`${predicate},${order}`] : []; 34 | if (fallback && predicate !== fallback) { 35 | sortParam.push(`${fallback},asc`); 36 | } 37 | return sortParam; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/webapp/bootstrap.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { bootstrapApplication } from '@angular/platform-browser'; 3 | import { appConfig } from './app/app.config'; 4 | import AppComponent from './app/app.component'; 5 | 6 | import { environment } from './environments/environment'; 7 | 8 | // disable debug data on prod profile to improve performance 9 | if (!environment.DEBUG_INFO_ENABLED) { 10 | enableProdMode(); 11 | } 12 | 13 | bootstrapApplication(AppComponent, appConfig) 14 | // eslint-disable-next-line no-console 15 | .then(() => console.log('Application started')) 16 | .catch((err: unknown) => console.error(err)); 17 | -------------------------------------------------------------------------------- /src/main/webapp/content/images/jhipster_family_member_0_head-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-gateway/bad6f8ce2f5441dda8d5759e1ef1904de635972c/src/main/webapp/content/images/jhipster_family_member_0_head-192.png -------------------------------------------------------------------------------- /src/main/webapp/content/images/jhipster_family_member_0_head-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-gateway/bad6f8ce2f5441dda8d5759e1ef1904de635972c/src/main/webapp/content/images/jhipster_family_member_0_head-256.png -------------------------------------------------------------------------------- /src/main/webapp/content/images/jhipster_family_member_0_head-384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-gateway/bad6f8ce2f5441dda8d5759e1ef1904de635972c/src/main/webapp/content/images/jhipster_family_member_0_head-384.png -------------------------------------------------------------------------------- /src/main/webapp/content/images/jhipster_family_member_0_head-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-gateway/bad6f8ce2f5441dda8d5759e1ef1904de635972c/src/main/webapp/content/images/jhipster_family_member_0_head-512.png -------------------------------------------------------------------------------- /src/main/webapp/content/images/jhipster_family_member_1_head-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-gateway/bad6f8ce2f5441dda8d5759e1ef1904de635972c/src/main/webapp/content/images/jhipster_family_member_1_head-192.png -------------------------------------------------------------------------------- /src/main/webapp/content/images/jhipster_family_member_1_head-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-gateway/bad6f8ce2f5441dda8d5759e1ef1904de635972c/src/main/webapp/content/images/jhipster_family_member_1_head-256.png -------------------------------------------------------------------------------- /src/main/webapp/content/images/jhipster_family_member_1_head-384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-gateway/bad6f8ce2f5441dda8d5759e1ef1904de635972c/src/main/webapp/content/images/jhipster_family_member_1_head-384.png -------------------------------------------------------------------------------- /src/main/webapp/content/images/jhipster_family_member_1_head-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-gateway/bad6f8ce2f5441dda8d5759e1ef1904de635972c/src/main/webapp/content/images/jhipster_family_member_1_head-512.png -------------------------------------------------------------------------------- /src/main/webapp/content/images/jhipster_family_member_2_head-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-gateway/bad6f8ce2f5441dda8d5759e1ef1904de635972c/src/main/webapp/content/images/jhipster_family_member_2_head-192.png -------------------------------------------------------------------------------- /src/main/webapp/content/images/jhipster_family_member_2_head-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-gateway/bad6f8ce2f5441dda8d5759e1ef1904de635972c/src/main/webapp/content/images/jhipster_family_member_2_head-256.png -------------------------------------------------------------------------------- /src/main/webapp/content/images/jhipster_family_member_2_head-384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-gateway/bad6f8ce2f5441dda8d5759e1ef1904de635972c/src/main/webapp/content/images/jhipster_family_member_2_head-384.png -------------------------------------------------------------------------------- /src/main/webapp/content/images/jhipster_family_member_2_head-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-gateway/bad6f8ce2f5441dda8d5759e1ef1904de635972c/src/main/webapp/content/images/jhipster_family_member_2_head-512.png -------------------------------------------------------------------------------- /src/main/webapp/content/images/jhipster_family_member_3_head-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-gateway/bad6f8ce2f5441dda8d5759e1ef1904de635972c/src/main/webapp/content/images/jhipster_family_member_3_head-192.png -------------------------------------------------------------------------------- /src/main/webapp/content/images/jhipster_family_member_3_head-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-gateway/bad6f8ce2f5441dda8d5759e1ef1904de635972c/src/main/webapp/content/images/jhipster_family_member_3_head-256.png -------------------------------------------------------------------------------- /src/main/webapp/content/images/jhipster_family_member_3_head-384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-gateway/bad6f8ce2f5441dda8d5759e1ef1904de635972c/src/main/webapp/content/images/jhipster_family_member_3_head-384.png -------------------------------------------------------------------------------- /src/main/webapp/content/images/jhipster_family_member_3_head-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-gateway/bad6f8ce2f5441dda8d5759e1ef1904de635972c/src/main/webapp/content/images/jhipster_family_member_3_head-512.png -------------------------------------------------------------------------------- /src/main/webapp/content/images/logo-jhipster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-gateway/bad6f8ce2f5441dda8d5759e1ef1904de635972c/src/main/webapp/content/images/logo-jhipster.png -------------------------------------------------------------------------------- /src/main/webapp/content/scss/_bootstrap-variables.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * Bootstrap overrides https://getbootstrap.com/docs/5.1/customize/sass/ 3 | * All values defined in bootstrap source 4 | * https://github.com/twbs/bootstrap/blob/v5.1.3/scss/_variables.scss can be overwritten here 5 | * Make sure not to add !default to values here 6 | */ 7 | 8 | // Options: 9 | // Quickly modify global styling by enabling or disabling optional features. 10 | $enable-rounded: true; 11 | $enable-shadows: false; 12 | $enable-gradients: false; 13 | $enable-transitions: true; 14 | $enable-hover-media-query: false; 15 | $enable-grid-classes: true; 16 | $enable-print-styles: true; 17 | 18 | // Components: 19 | // Define common padding and border radius sizes and more. 20 | 21 | $border-radius: 0.15rem; 22 | $border-radius-lg: 0.125rem; 23 | $border-radius-sm: 0.1rem; 24 | 25 | // Body: 26 | // Settings for the `` element. 27 | 28 | $body-bg: #ffffff; 29 | 30 | // Typography: 31 | // Font, line-height, and color for body text, headings, and more. 32 | 33 | $font-size-base: 1rem; 34 | 35 | $dropdown-link-hover-color: white; 36 | $dropdown-link-hover-bg: #343a40; 37 | -------------------------------------------------------------------------------- /src/main/webapp/content/scss/vendor.scss: -------------------------------------------------------------------------------- 1 | /* after changing this file run 'npm run webapp:build' */ 2 | 3 | /*************************** 4 | put Sass variables here: 5 | eg $input-color: red; 6 | ****************************/ 7 | // Override Bootstrap variables 8 | @import 'bootstrap-variables'; 9 | // Import Bootstrap source files from node_modules 10 | @import 'bootstrap/scss/bootstrap'; 11 | 12 | /* jhipster-needle-scss-add-vendor JHipster will add new css style */ 13 | -------------------------------------------------------------------------------- /src/main/webapp/declarations.d.ts: -------------------------------------------------------------------------------- 1 | // These constants are injected via webpack DefinePlugin variables. 2 | // You can add more variables in webpack.common.js or in profile specific webpack..js files. 3 | // If you change the values in the webpack config files, you need to re run webpack to update the application 4 | 5 | declare const __VERSION__: string; 6 | declare const SERVER_API_URL: string; 7 | -------------------------------------------------------------------------------- /src/main/webapp/environments/environment.development.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | VERSION: 'DEV', 3 | DEBUG_INFO_ENABLED: true, 4 | }; 5 | -------------------------------------------------------------------------------- /src/main/webapp/environments/environment.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | VERSION: __VERSION__, 3 | DEBUG_INFO_ENABLED: false, 4 | }; 5 | -------------------------------------------------------------------------------- /src/main/webapp/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-gateway/bad6f8ce2f5441dda8d5759e1ef1904de635972c/src/main/webapp/favicon.ico -------------------------------------------------------------------------------- /src/main/webapp/main.ts: -------------------------------------------------------------------------------- 1 | import('./bootstrap').catch((err: unknown) => console.error(err)); 2 | -------------------------------------------------------------------------------- /src/main/webapp/manifest.webapp: -------------------------------------------------------------------------------- 1 | { 2 | "name": "JhipsterSampleGateway", 3 | "short_name": "JhipsterSampleGateway", 4 | "icons": [ 5 | { 6 | "src": "./content/images/jhipster_family_member_1_head-192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "./content/images/jhipster_family_member_1_head-256.png", 12 | "sizes": "256x256", 13 | "type": "image/png" 14 | }, 15 | { 16 | "src": "./content/images/jhipster_family_member_1_head-384.png", 17 | "sizes": "384x384", 18 | "type": "image/png" 19 | }, 20 | { 21 | "src": "./content/images/jhipster_family_member_1_head-512.png", 22 | "sizes": "512x512", 23 | "type": "image/png" 24 | } 25 | ], 26 | "theme_color": "#000000", 27 | "background_color": "#e0e0e0", 28 | "start_url": ".", 29 | "display": "standalone", 30 | "orientation": "portrait" 31 | } 32 | -------------------------------------------------------------------------------- /src/main/webapp/robots.txt: -------------------------------------------------------------------------------- 1 | # robotstxt.org/ 2 | 3 | User-agent: * 4 | Disallow: /api/account 5 | Disallow: /api/account/change-password 6 | Disallow: /api/account/sessions 7 | Disallow: /api/logs/ 8 | Disallow: /api/users/ 9 | Disallow: /management/ 10 | Disallow: /v3/api-docs/ 11 | -------------------------------------------------------------------------------- /src/test/gatling/conf/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | %d{HH:mm:ss.SSS} [%-5level] %logger{15} - %msg%n%rEx 9 | false 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/test/java/io/github/jhipster/sample/IntegrationTest.java: -------------------------------------------------------------------------------- 1 | package io.github.jhipster.sample; 2 | 3 | import io.github.jhipster.sample.config.AsyncSyncConfiguration; 4 | import io.github.jhipster.sample.config.EmbeddedSQL; 5 | import io.github.jhipster.sample.config.JacksonConfiguration; 6 | import java.lang.annotation.ElementType; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.RetentionPolicy; 9 | import java.lang.annotation.Target; 10 | import org.springframework.boot.test.context.SpringBootTest; 11 | 12 | /** 13 | * Base composite annotation for integration tests. 14 | */ 15 | @Target(ElementType.TYPE) 16 | @Retention(RetentionPolicy.RUNTIME) 17 | @SpringBootTest(classes = { JhipsterSampleGatewayApp.class, JacksonConfiguration.class, AsyncSyncConfiguration.class }) 18 | @EmbeddedSQL 19 | public @interface IntegrationTest { 20 | // 5s is Spring's default https://github.com/spring-projects/spring-framework/blob/main/spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClient.java#L106 21 | String DEFAULT_TIMEOUT = "PT5S"; 22 | 23 | String DEFAULT_ENTITY_TIMEOUT = "PT5S"; 24 | } 25 | -------------------------------------------------------------------------------- /src/test/java/io/github/jhipster/sample/config/AsyncSyncConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.github.jhipster.sample.config; 2 | 3 | import java.util.concurrent.Executor; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.core.task.SyncTaskExecutor; 7 | 8 | @Configuration 9 | public class AsyncSyncConfiguration { 10 | 11 | @Bean(name = "taskExecutor") 12 | public Executor taskExecutor() { 13 | return new SyncTaskExecutor(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/test/java/io/github/jhipster/sample/config/EmbeddedSQL.java: -------------------------------------------------------------------------------- 1 | package io.github.jhipster.sample.config; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Target(ElementType.TYPE) 9 | @Retention(RetentionPolicy.RUNTIME) 10 | public @interface EmbeddedSQL { 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/io/github/jhipster/sample/config/JHipsterBlockHoundIntegration.java: -------------------------------------------------------------------------------- 1 | package io.github.jhipster.sample.config; 2 | 3 | import reactor.blockhound.BlockHound; 4 | import reactor.blockhound.integration.BlockHoundIntegration; 5 | 6 | public class JHipsterBlockHoundIntegration implements BlockHoundIntegration { 7 | 8 | @Override 9 | public void applyTo(BlockHound.Builder builder) { 10 | builder.allowBlockingCallsInside("org.springframework.validation.beanvalidation.SpringValidatorAdapter", "validate"); 11 | builder.allowBlockingCallsInside("io.github.jhipster.sample.service.MailService", "sendEmailFromTemplate"); 12 | builder.allowBlockingCallsInside("io.github.jhipster.sample.security.DomainUserDetailsService", "createSpringSecurityUser"); 13 | builder.allowBlockingCallsInside("org.springframework.web.reactive.result.method.InvocableHandlerMethod", "invoke"); 14 | builder.allowBlockingCallsInside("org.springdoc.core.service.OpenAPIService", "build"); 15 | builder.allowBlockingCallsInside("org.springdoc.core.service.OpenAPIService", "getWebhooks"); 16 | builder.allowBlockingCallsInside("org.springdoc.core.service.AbstractRequestService", "build"); 17 | // jhipster-needle-blockhound-integration - JHipster will add additional gradle plugins here 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/test/java/io/github/jhipster/sample/config/MysqlTestContainer.java: -------------------------------------------------------------------------------- 1 | package io.github.jhipster.sample.config; 2 | 3 | import java.util.Collections; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.testcontainers.containers.JdbcDatabaseContainer; 7 | import org.testcontainers.containers.MySQLContainer; 8 | import org.testcontainers.containers.output.Slf4jLogConsumer; 9 | 10 | public class MysqlTestContainer implements SqlTestContainer { 11 | 12 | private static final Logger LOG = LoggerFactory.getLogger(MysqlTestContainer.class); 13 | 14 | private MySQLContainer mysqlContainer; 15 | 16 | @Override 17 | public void destroy() { 18 | if (null != mysqlContainer && mysqlContainer.isRunning()) { 19 | mysqlContainer.stop(); 20 | } 21 | } 22 | 23 | @Override 24 | public void afterPropertiesSet() { 25 | if (null == mysqlContainer) { 26 | mysqlContainer = new MySQLContainer<>("mysql:9.2.0") 27 | .withDatabaseName("jhipsterSampleGateway") 28 | .withTmpFs(Collections.singletonMap("/testtmpfs", "rw")) 29 | .withLogConsumer(new Slf4jLogConsumer(LOG)) 30 | .withReuse(true); 31 | } 32 | if (!mysqlContainer.isRunning()) { 33 | mysqlContainer.start(); 34 | } 35 | } 36 | 37 | @Override 38 | public JdbcDatabaseContainer getTestContainer() { 39 | return mysqlContainer; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/io/github/jhipster/sample/config/SpringBootTestClassOrderer.java: -------------------------------------------------------------------------------- 1 | package io.github.jhipster.sample.config; 2 | 3 | import io.github.jhipster.sample.IntegrationTest; 4 | import java.util.Comparator; 5 | import org.junit.jupiter.api.ClassDescriptor; 6 | import org.junit.jupiter.api.ClassOrderer; 7 | import org.junit.jupiter.api.ClassOrdererContext; 8 | 9 | public class SpringBootTestClassOrderer implements ClassOrderer { 10 | 11 | @Override 12 | public void orderClasses(ClassOrdererContext context) { 13 | context.getClassDescriptors().sort(Comparator.comparingInt(SpringBootTestClassOrderer::getOrder)); 14 | } 15 | 16 | private static int getOrder(ClassDescriptor classDescriptor) { 17 | if (classDescriptor.findAnnotation(IntegrationTest.class).isPresent()) { 18 | return 2; 19 | } 20 | return 1; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/test/java/io/github/jhipster/sample/config/SqlTestContainer.java: -------------------------------------------------------------------------------- 1 | package io.github.jhipster.sample.config; 2 | 3 | import org.springframework.beans.factory.DisposableBean; 4 | import org.springframework.beans.factory.InitializingBean; 5 | import org.testcontainers.containers.JdbcDatabaseContainer; 6 | 7 | public interface SqlTestContainer extends InitializingBean, DisposableBean { 8 | JdbcDatabaseContainer getTestContainer(); 9 | } 10 | -------------------------------------------------------------------------------- /src/test/java/io/github/jhipster/sample/domain/AssertUtils.java: -------------------------------------------------------------------------------- 1 | package io.github.jhipster.sample.domain; 2 | 3 | import java.math.BigDecimal; 4 | import java.time.ZoneOffset; 5 | import java.time.ZonedDateTime; 6 | import java.util.Comparator; 7 | 8 | public class AssertUtils { 9 | 10 | public static Comparator zonedDataTimeSameInstant = Comparator.nullsFirst((e1, a2) -> 11 | e1.withZoneSameInstant(ZoneOffset.UTC).compareTo(a2.withZoneSameInstant(ZoneOffset.UTC)) 12 | ); 13 | 14 | public static Comparator bigDecimalCompareTo = Comparator.nullsFirst(BigDecimal::compareTo); 15 | } 16 | -------------------------------------------------------------------------------- /src/test/java/io/github/jhipster/sample/domain/AuthorityTest.java: -------------------------------------------------------------------------------- 1 | package io.github.jhipster.sample.domain; 2 | 3 | import static io.github.jhipster.sample.domain.AuthorityTestSamples.*; 4 | import static org.assertj.core.api.Assertions.assertThat; 5 | 6 | import io.github.jhipster.sample.web.rest.TestUtil; 7 | import org.junit.jupiter.api.Test; 8 | 9 | class AuthorityTest { 10 | 11 | @Test 12 | void equalsVerifier() throws Exception { 13 | TestUtil.equalsVerifier(Authority.class); 14 | Authority authority1 = getAuthoritySample1(); 15 | Authority authority2 = new Authority(); 16 | assertThat(authority1).isNotEqualTo(authority2); 17 | 18 | authority2.setName(authority1.getName()); 19 | assertThat(authority1).isEqualTo(authority2); 20 | 21 | authority2 = getAuthoritySample2(); 22 | assertThat(authority1).isNotEqualTo(authority2); 23 | } 24 | 25 | @Test 26 | void hashCodeVerifier() { 27 | Authority authority = new Authority(); 28 | assertThat(authority.hashCode()).isZero(); 29 | 30 | Authority authority1 = getAuthoritySample1(); 31 | authority.setName(authority1.getName()); 32 | assertThat(authority).hasSameHashCodeAs(authority1); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/io/github/jhipster/sample/domain/AuthorityTestSamples.java: -------------------------------------------------------------------------------- 1 | package io.github.jhipster.sample.domain; 2 | 3 | import java.util.UUID; 4 | 5 | public class AuthorityTestSamples { 6 | 7 | public static Authority getAuthoritySample1() { 8 | return new Authority().name("name1"); 9 | } 10 | 11 | public static Authority getAuthoritySample2() { 12 | return new Authority().name("name2"); 13 | } 14 | 15 | public static Authority getAuthorityRandomSampleGenerator() { 16 | return new Authority().name(UUID.randomUUID().toString()); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/test/java/io/github/jhipster/sample/web/filter/SpaWebFilterTestController.java: -------------------------------------------------------------------------------- 1 | package io.github.jhipster.sample.web.filter; 2 | 3 | import org.springframework.http.MediaType; 4 | import org.springframework.web.bind.annotation.GetMapping; 5 | import org.springframework.web.bind.annotation.RestController; 6 | 7 | @RestController 8 | public class SpaWebFilterTestController { 9 | 10 | public static final String INDEX_HTML_TEST_CONTENT = "test"; 11 | 12 | @GetMapping(value = "/index.html", produces = MediaType.TEXT_HTML_VALUE) 13 | public String getIndexHtmlTestContent() { 14 | return INDEX_HTML_TEST_CONTENT; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/test/java/io/github/jhipster/sample/web/rest/WithUnauthenticatedMockUser.java: -------------------------------------------------------------------------------- 1 | package io.github.jhipster.sample.web.rest; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | import org.springframework.security.core.context.SecurityContext; 8 | import org.springframework.security.core.context.SecurityContextHolder; 9 | import org.springframework.security.test.context.support.WithSecurityContext; 10 | import org.springframework.security.test.context.support.WithSecurityContextFactory; 11 | 12 | @Target({ ElementType.METHOD, ElementType.TYPE }) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @WithSecurityContext(factory = WithUnauthenticatedMockUser.Factory.class) 15 | public @interface WithUnauthenticatedMockUser { 16 | class Factory implements WithSecurityContextFactory { 17 | 18 | @Override 19 | public SecurityContext createSecurityContext(WithUnauthenticatedMockUser annotation) { 20 | return SecurityContextHolder.createEmptyContext(); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/test/javascript/cypress/e2e/account/logout.cy.ts: -------------------------------------------------------------------------------- 1 | import { accountMenuSelector, loginItemSelector, navbarSelector } from '../../support/commands'; 2 | 3 | describe('logout', () => { 4 | const username = Cypress.env('E2E_USERNAME') ?? 'user'; 5 | const password = Cypress.env('E2E_PASSWORD') ?? 'user'; 6 | 7 | it('go to home page when successfully logs out', () => { 8 | cy.login(username, password); 9 | cy.visit(''); 10 | 11 | cy.clickOnLogoutItem(); 12 | 13 | cy.get(navbarSelector).get(accountMenuSelector).click(); 14 | cy.get(navbarSelector).get(accountMenuSelector).get(loginItemSelector).should('be.visible'); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/test/javascript/cypress/e2e/account/reset-password-page.cy.ts: -------------------------------------------------------------------------------- 1 | import { 2 | classInvalid, 3 | classValid, 4 | emailResetPasswordSelector, 5 | forgetYourPasswordSelector, 6 | submitInitResetPasswordSelector, 7 | usernameLoginSelector, 8 | } from '../../support/commands'; 9 | 10 | describe('forgot your password', () => { 11 | const username = Cypress.env('E2E_USERNAME') ?? 'user'; 12 | 13 | beforeEach(() => { 14 | cy.visit(''); 15 | cy.clickOnLoginItem(); 16 | cy.get(usernameLoginSelector).type(username); 17 | cy.get(forgetYourPasswordSelector).click(); 18 | }); 19 | 20 | beforeEach(() => { 21 | cy.intercept('POST', '/api/account/reset-password/init').as('initResetPassword'); 22 | }); 23 | 24 | it('requires email', () => { 25 | cy.get(emailResetPasswordSelector).should('have.class', classInvalid); 26 | cy.get(emailResetPasswordSelector).type('user@gmail.com'); 27 | cy.get(emailResetPasswordSelector).should('have.class', classValid); 28 | }); 29 | 30 | it('should be able to init reset password', () => { 31 | cy.get(emailResetPasswordSelector).type('user@gmail.com'); 32 | cy.get(submitInitResetPasswordSelector).click({ force: true }); 33 | cy.wait('@initResetPassword').then(({ response }) => expect(response?.statusCode).to.equal(200)); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /src/test/javascript/cypress/e2e/lighthouse.audits.ts: -------------------------------------------------------------------------------- 1 | describe('Lighthouse Audits', () => { 2 | beforeEach(() => { 3 | cy.visit('/'); 4 | }); 5 | 6 | it('homepage', () => { 7 | const customThresholds = { 8 | performance: 80, 9 | accessibility: 90, 10 | seo: 90, 11 | 'best-practices': 90, 12 | // If you have enabled PWA you should set this threshold to 100 13 | pwa: 0, 14 | }; 15 | 16 | const desktopConfig = { 17 | extends: 'lighthouse:default', 18 | formFactor: 'desktop', 19 | // Change the CPU slowdown multiplier to emulate different kind of devices 20 | // See https://github.com/GoogleChrome/lighthouse/blob/master/docs/throttling.md#cpu-throttling for details 21 | throttling: { 22 | cpuSlowdownMultiplier: 1, 23 | }, 24 | screenEmulation: { disabled: true }, 25 | }; 26 | cy.lighthouse(customThresholds, desktopConfig); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /src/test/javascript/cypress/fixtures/integration-test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-gateway/bad6f8ce2f5441dda8d5759e1ef1904de635972c/src/test/javascript/cypress/fixtures/integration-test.png -------------------------------------------------------------------------------- /src/test/javascript/cypress/support/account.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-namespace */ 2 | export type Account = Record; 3 | 4 | Cypress.Commands.add('getAccount', () => { 5 | return cy 6 | .authenticatedRequest({ 7 | method: 'GET', 8 | url: '/api/account', 9 | }) 10 | .then(response => response.body as Account); 11 | }); 12 | 13 | Cypress.Commands.add('saveAccount', (account: Account) => { 14 | return cy.authenticatedRequest({ 15 | method: 'POST', 16 | url: '/api/account', 17 | body: account, 18 | }); 19 | }); 20 | 21 | declare global { 22 | namespace Cypress { 23 | interface Chainable { 24 | getAccount(): Cypress.Chainable; 25 | saveAccount(account: Account): Cypress.Chainable>; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/test/javascript/cypress/support/index.ts: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | import './account'; 17 | import './commands'; 18 | import './navbar'; 19 | import './entity'; 20 | import './management'; 21 | -------------------------------------------------------------------------------- /src/test/javascript/cypress/support/management.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-namespace */ 2 | /* eslint-disable @typescript-eslint/no-unsafe-return */ 3 | 4 | Cypress.Commands.add('getManagementInfo', () => { 5 | return cy 6 | .request({ 7 | method: 'GET', 8 | url: '/management/info', 9 | }) 10 | .then(response => response.body); 11 | }); 12 | 13 | declare global { 14 | namespace Cypress { 15 | interface Chainable { 16 | getManagementInfo(): Cypress.Chainable; 17 | } 18 | } 19 | } 20 | 21 | // Convert this to a module instead of a script (allows import/export) 22 | export {}; 23 | -------------------------------------------------------------------------------- /src/test/javascript/cypress/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../../tsconfig.json", 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "sourceMap": false, 6 | "outDir": "../../../../target/cypress/tsc", 7 | "target": "es2018", 8 | "types": ["cypress", "node"] 9 | }, 10 | "include": ["./../../../../cypress*.config.ts", "./**/*.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /src/test/resources/META-INF/services/reactor.blockhound.integration.BlockHoundIntegration: -------------------------------------------------------------------------------- 1 | io.github.jhipster.sample.config.JHipsterBlockHoundIntegration 2 | -------------------------------------------------------------------------------- /src/test/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.test.context.ContextCustomizerFactory = \ 2 | io.github.jhipster.sample.config.\ 3 | SqlTestContainersSpringContextCustomizerFactory -------------------------------------------------------------------------------- /src/test/resources/config/application-testdev.yml: -------------------------------------------------------------------------------- 1 | # =================================================================== 2 | # Spring Boot configuration. 3 | # 4 | # This configuration is used for unit/integration tests with testcontainers database containers. 5 | # 6 | # To activate this configuration launch integration tests with the 'testcontainers' profile 7 | # 8 | # More information on database containers: https://www.testcontainers.org/modules/databases/ 9 | # =================================================================== 10 | 11 | spring: 12 | r2dbc: 13 | url: r2dbc:h2:file:///./target/h2db/testdb/jhipstersamplegateway;DB_CLOSE_DELAY=-1;MODE=MYSQL 14 | username: jhipstersamplegateway 15 | password: 16 | liquibase: 17 | url: jdbc:h2:file:./target/h2db/testdb/jhipstersamplegateway;DB_CLOSE_DELAY=-1;MODE=MYSQL 18 | -------------------------------------------------------------------------------- /src/test/resources/config/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | cloud: 3 | consul: 4 | discovery: 5 | enabled: false 6 | instanceId: ${spring.application.name}:${spring.application.instance-id:${random.value}} 7 | config: 8 | enabled: false 9 | enabled: false 10 | -------------------------------------------------------------------------------- /src/test/resources/i18n/messages_en.properties: -------------------------------------------------------------------------------- 1 | email.test.title=test title 2 | -------------------------------------------------------------------------------- /src/test/resources/junit-platform.properties: -------------------------------------------------------------------------------- 1 | junit.jupiter.execution.timeout.default = 15 s 2 | junit.jupiter.execution.timeout.testable.method.default = 15 s 3 | junit.jupiter.execution.timeout.beforeall.method.default = 60 s 4 | junit.jupiter.testclass.order.default=io.github.jhipster.sample.config.SpringBootTestClassOrderer 5 | -------------------------------------------------------------------------------- /src/test/resources/templates/mail/activationEmail.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | JHipster activation 5 | 6 | 7 | 8 |

Dear

9 |

Your JHipster account has been created, please click on the URL below to activate it:

10 |

11 | Activation link 12 |

13 |

14 | Regards, 15 |
16 | JHipster. 17 |

18 | 19 | 20 | -------------------------------------------------------------------------------- /src/test/resources/templates/mail/creationEmail.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | JHipster creation 5 | 6 | 7 | 8 |

Dear

9 |

Your JHipster account has been created, please click on the URL below to access it:

10 |

11 | Login link 12 |

13 |

14 | Regards, 15 |
16 | JHipster. 17 |

18 | 19 | 20 | -------------------------------------------------------------------------------- /src/test/resources/templates/mail/passwordResetEmail.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | JHipster password reset 5 | 6 | 7 | 8 |

Dear

9 |

10 | For your JHipster account a password reset was requested, please click on the URL below to reset it: 11 |

12 |

13 | Login link 14 |

15 |

16 | Regards, 17 |
18 | JHipster. 19 |

20 | 21 | 22 | -------------------------------------------------------------------------------- /src/test/resources/templates/mail/testEmail.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./target/out-tsc/app", 5 | "types": ["@angular/localize"] 6 | }, 7 | "files": ["src/main/webapp/main.ts"], 8 | "include": ["src/main/webapp/**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "src/main/webapp/", 4 | "outDir": "./target/out-tsc/root", 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "strictNullChecks": true, 8 | "noImplicitReturns": true, 9 | "noFallthroughCasesInSwitch": true, 10 | "sourceMap": true, 11 | "declaration": false, 12 | "downlevelIteration": true, 13 | "experimentalDecorators": true, 14 | "moduleResolution": "node", 15 | "importHelpers": true, 16 | "esModuleInterop": true, 17 | "allowSyntheticDefaultImports": true, 18 | "useDefineForClassFields": false, 19 | "target": "es2022", 20 | "module": "es2020", 21 | "types": [], 22 | "lib": ["es2018", "es2020", "dom"] 23 | }, 24 | "references": [ 25 | { 26 | "path": "tsconfig.spec.json" 27 | } 28 | ], 29 | "angularCompilerOptions": { 30 | "strictInjectionParameters": true, 31 | "strictInputAccessModifiers": true, 32 | "strictTemplates": true, 33 | "preserveWhitespaces": true 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src/main/webapp/**/*.ts"], 4 | "compilerOptions": { 5 | "composite": true, 6 | "outDir": "target/out-tsc/spec", 7 | "types": ["jest", "node"] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /webpack/environment.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | I18N_HASH: 'generated_hash', 3 | SERVER_API_URL: '', 4 | __VERSION__: process.env.APP_VERSION || 'DEV', 5 | }; 6 | -------------------------------------------------------------------------------- /webpack/logo-jhipster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhipster/jhipster-sample-app-gateway/bad6f8ce2f5441dda8d5759e1ef1904de635972c/webpack/logo-jhipster.png -------------------------------------------------------------------------------- /webpack/proxy.conf.js: -------------------------------------------------------------------------------- 1 | function setupProxy({ tls }) { 2 | const serverResources = ['/api', '/services', '/management', '/v3/api-docs', '/h2-console', '/health']; 3 | return [ 4 | { 5 | context: serverResources, 6 | target: `http${tls ? 's' : ''}://localhost:8080`, 7 | secure: false, 8 | changeOrigin: tls, 9 | }, 10 | ]; 11 | } 12 | 13 | module.exports = setupProxy; 14 | --------------------------------------------------------------------------------