├── client
├── src
│ └── main
│ │ └── ng
│ │ ├── src
│ │ ├── assets
│ │ │ ├── global.scss
│ │ │ ├── favicon.ico
│ │ │ └── reset.scss
│ │ ├── app
│ │ │ ├── feature
│ │ │ │ ├── page-not-found
│ │ │ │ │ ├── page-not-found.component.html
│ │ │ │ │ └── page-not-found.component.ts
│ │ │ │ ├── layout
│ │ │ │ │ ├── home
│ │ │ │ │ │ ├── home.component.html
│ │ │ │ │ │ └── home.component.ts
│ │ │ │ │ ├── lazy
│ │ │ │ │ │ ├── lazy.component.ts
│ │ │ │ │ │ └── lazy.module.ts
│ │ │ │ │ ├── layout.component.html
│ │ │ │ │ ├── layout.component.ts
│ │ │ │ │ ├── layout.module.ts
│ │ │ │ │ └── layout.route-module.ts
│ │ │ │ └── login
│ │ │ │ │ ├── login.component.html
│ │ │ │ │ ├── login.module.ts
│ │ │ │ │ ├── login.component.scss
│ │ │ │ │ └── login.component.ts
│ │ │ ├── model
│ │ │ │ └── credentials.model.ts
│ │ │ ├── util
│ │ │ │ └── api.const.ts
│ │ │ ├── app.component.ts
│ │ │ ├── shared
│ │ │ │ └── shared.module.ts
│ │ │ ├── core
│ │ │ │ ├── interceptor
│ │ │ │ │ ├── xhr.interceptor.ts
│ │ │ │ │ └── auth.interceptor.ts
│ │ │ │ ├── service
│ │ │ │ │ ├── echo.service.ts
│ │ │ │ │ ├── abstract-service.ts
│ │ │ │ │ └── auth.service.ts
│ │ │ │ ├── core.module.ts
│ │ │ │ └── guard
│ │ │ │ │ └── auth.guard.ts
│ │ │ ├── app-routing.module.ts
│ │ │ └── app.module.ts
│ │ ├── environments
│ │ │ ├── environment.prod.ts
│ │ │ └── environment.ts
│ │ ├── typings.d.ts
│ │ ├── tsconfig.app.json
│ │ ├── index.html
│ │ ├── main.ts
│ │ ├── tsconfig.spec.json
│ │ ├── test.ts
│ │ └── polyfills.ts
│ │ ├── proxy.conf.json
│ │ ├── .gitignore
│ │ ├── e2e
│ │ ├── app.po.ts
│ │ ├── tsconfig.e2e.json
│ │ └── app.e2e-spec.ts
│ │ ├── .editorconfig
│ │ ├── tsconfig.json
│ │ ├── protractor.conf.js
│ │ ├── karma.conf.js
│ │ ├── package.json
│ │ ├── tslint.json
│ │ └── angular.json
└── pom.xml
├── server
├── src
│ └── main
│ │ ├── resources
│ │ ├── application.yml
│ │ └── log4j2.xml
│ │ └── kotlin
│ │ └── com
│ │ └── hiper2d
│ │ ├── controller
│ │ ├── EchoController.kt
│ │ └── UserController.kt
│ │ ├── Application.kt
│ │ ├── config
│ │ ├── WebConfig.kt
│ │ └── WebSecurityConfig.kt
│ │ └── security
│ │ └── provider
│ │ └── AnyAuthenticationProvider.kt
└── pom.xml
├── .gitignore
├── .travis.yml
├── pom.xml
└── README.md
/client/src/main/ng/src/assets/global.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/server/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | server.port: 9001
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.idea/
2 | /**/target/
3 | /**/*.log
4 | /**/*.iml
5 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 | notifications:
3 | email: false
4 |
--------------------------------------------------------------------------------
/client/src/main/ng/src/app/feature/page-not-found/page-not-found.component.html:
--------------------------------------------------------------------------------
1 | Page not found 404
--------------------------------------------------------------------------------
/client/src/main/ng/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/client/src/main/ng/proxy.conf.json:
--------------------------------------------------------------------------------
1 | {
2 | "/api/**": {
3 | "target": "http://localhost:9001",
4 | "secure": false
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/client/src/main/ng/src/app/feature/layout/home/home.component.html:
--------------------------------------------------------------------------------
1 |
Hi, {{ username }}
2 | Echo from server: {{echo}}
3 |
--------------------------------------------------------------------------------
/client/src/main/ng/src/app/model/credentials.model.ts:
--------------------------------------------------------------------------------
1 | export interface Credentials {
2 | username: string;
3 | password: string;
4 | }
5 |
--------------------------------------------------------------------------------
/client/src/main/ng/src/assets/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiper2d/spring-boot-angular-maven-starter/HEAD/client/src/main/ng/src/assets/favicon.ico
--------------------------------------------------------------------------------
/client/src/main/ng/src/typings.d.ts:
--------------------------------------------------------------------------------
1 | /* SystemJS module definition */
2 | declare var module: NodeModule;
3 | interface NodeModule {
4 | id: string;
5 | }
6 |
--------------------------------------------------------------------------------
/client/src/main/ng/.gitignore:
--------------------------------------------------------------------------------
1 | # compiled output
2 | /dist/
3 |
4 | # dependencies
5 | /node/
6 | /node_modules/
7 |
8 | # e2e
9 | /e2e/*.js
10 | /e2e/*.map
11 |
--------------------------------------------------------------------------------
/client/src/main/ng/src/app/util/api.const.ts:
--------------------------------------------------------------------------------
1 | export class ApiConst {
2 | static readonly ECHO = '/api/echo';
3 | static readonly LOGOUT = '/api/logout';
4 | static readonly USER = '/api/user';
5 | }
6 |
--------------------------------------------------------------------------------
/client/src/main/ng/src/app/feature/layout/lazy/lazy.component.ts:
--------------------------------------------------------------------------------
1 | import {Component} from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-lazy',
5 | template: `I'm Lazy
`
6 | })
7 | export class LazyComponent {
8 | }
9 |
--------------------------------------------------------------------------------
/client/src/main/ng/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import {Component} from '@angular/core';
2 | import '../assets/reset.scss';
3 |
4 | @Component({
5 | selector: 'app-root',
6 | template: ''
7 | })
8 | export class AppComponent {
9 | }
10 |
--------------------------------------------------------------------------------
/client/src/main/ng/e2e/app.po.ts:
--------------------------------------------------------------------------------
1 | import { browser, by, element } from 'protractor';
2 |
3 | export class AppPage {
4 | navigateTo() {
5 | return browser.get('/');
6 | }
7 |
8 | getParagraphText() {
9 | return element(by.css('app-root h1')).getText();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/client/src/main/ng/src/app/feature/page-not-found/page-not-found.component.ts:
--------------------------------------------------------------------------------
1 | import {Component} from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-page-not-found',
5 | templateUrl: './page-not-found.component.html'
6 | })
7 |
8 | export class PageNotFoundComponent {
9 | }
10 |
--------------------------------------------------------------------------------
/client/src/main/ng/src/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/app",
5 | "baseUrl": "./",
6 | "module": "es2015",
7 | "types": []
8 | },
9 | "exclude": [
10 | "test.ts",
11 | "**/*.spec.ts"
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/client/src/main/ng/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | max_line_length = off
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/client/src/main/ng/e2e/tsconfig.e2e.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/e2e",
5 | "baseUrl": "./",
6 | "module": "commonjs",
7 | "target": "es5",
8 | "types": [
9 | "jasmine",
10 | "jasminewd2",
11 | "node"
12 | ]
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/client/src/main/ng/src/app/feature/layout/layout.component.html:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/client/src/main/ng/src/app/feature/login/login.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/client/src/main/ng/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SpringBootAngularMavenStarter
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/client/src/main/ng/e2e/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { AppPage } from './app.po';
2 |
3 | describe('spring-boot-angular-maven-starter App', () => {
4 | let page: AppPage;
5 |
6 | beforeEach(() => {
7 | page = new AppPage();
8 | });
9 |
10 | it('should display welcome message', () => {
11 | page.navigateTo();
12 | expect(page.getParagraphText()).toEqual('Welcome to app!');
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/client/src/main/ng/src/app/feature/login/login.module.ts:
--------------------------------------------------------------------------------
1 | import {NgModule} from '@angular/core';
2 | import {SharedModule} from '../../shared/shared.module';
3 | import {LoginComponent} from './login.component';
4 |
5 | @NgModule({
6 | imports: [
7 | SharedModule
8 | ],
9 | declarations: [
10 | LoginComponent
11 | ],
12 | exports: [
13 | LoginComponent
14 | ]
15 | })
16 | export class LoginModule {
17 | }
18 |
--------------------------------------------------------------------------------
/server/src/main/kotlin/com/hiper2d/controller/EchoController.kt:
--------------------------------------------------------------------------------
1 | package com.hiper2d.controller
2 |
3 | import org.springframework.web.bind.annotation.GetMapping
4 | import org.springframework.web.bind.annotation.RequestMapping
5 | import org.springframework.web.bind.annotation.RestController
6 |
7 | @RestController
8 | @RequestMapping("/api/echo")
9 | class EchoController {
10 |
11 | @GetMapping
12 | fun echo() = "hi"
13 | }
--------------------------------------------------------------------------------
/client/src/main/ng/src/main.ts:
--------------------------------------------------------------------------------
1 | import { enableProdMode } from '@angular/core';
2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
3 |
4 | import { AppModule } from './app/app.module';
5 | import { environment } from './environments/environment';
6 |
7 | if (environment.production) {
8 | enableProdMode();
9 | }
10 |
11 | platformBrowserDynamic().bootstrapModule(AppModule)
12 | .catch(err => console.log(err));
13 |
--------------------------------------------------------------------------------
/client/src/main/ng/src/app/shared/shared.module.ts:
--------------------------------------------------------------------------------
1 | import {CommonModule} from '@angular/common';
2 | import {NgModule} from '@angular/core';
3 | import {ReactiveFormsModule} from '@angular/forms';
4 |
5 | @NgModule({
6 | imports: [
7 | CommonModule,
8 | ReactiveFormsModule
9 | ],
10 | declarations: [
11 | ],
12 | exports: [
13 | CommonModule,
14 | ReactiveFormsModule
15 | ]
16 | })
17 | export class SharedModule {
18 | }
19 |
--------------------------------------------------------------------------------
/client/src/main/ng/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // The file contents for the current environment will overwrite these during build.
2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do
3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead.
4 | // The list of which env maps to which file can be found in `.angular-cli.json`.
5 |
6 | export const environment = {
7 | production: false
8 | };
9 |
--------------------------------------------------------------------------------
/client/src/main/ng/src/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/spec",
5 | "baseUrl": "./",
6 | "module": "commonjs",
7 | "target": "es5",
8 | "types": [
9 | "jasmine",
10 | "node"
11 | ]
12 | },
13 | "files": [
14 | "test.ts",
15 | "polyfills.ts"
16 | ],
17 | "include": [
18 | "**/*.spec.ts",
19 | "**/*.d.ts"
20 | ]
21 | }
22 |
--------------------------------------------------------------------------------
/client/src/main/ng/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "outDir": "./dist/out-tsc",
5 | "sourceMap": true,
6 | "declaration": false,
7 | "moduleResolution": "node",
8 | "emitDecoratorMetadata": true,
9 | "experimentalDecorators": true,
10 | "target": "es5",
11 | "typeRoots": [
12 | "node_modules/@types"
13 | ],
14 | "lib": [
15 | "es2017",
16 | "dom"
17 | ]
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/client/src/main/ng/src/app/core/interceptor/xhr.interceptor.ts:
--------------------------------------------------------------------------------
1 | import {Injectable} from '@angular/core';
2 | import {HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
3 |
4 | @Injectable()
5 | export class XhrInterceptor implements HttpInterceptor {
6 |
7 | intercept(req: HttpRequest, next: HttpHandler) {
8 | const xhr = req.clone({
9 | headers: req.headers.set('X-Requested-With', 'XMLHttpRequest')
10 | });
11 | return next.handle(xhr);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/server/src/main/resources/log4j2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/server/src/main/kotlin/com/hiper2d/Application.kt:
--------------------------------------------------------------------------------
1 | package com.hiper2d
2 |
3 | import org.springframework.boot.SpringApplication
4 | import org.springframework.boot.autoconfigure.SpringBootApplication
5 | import org.springframework.boot.builder.SpringApplicationBuilder
6 | import org.springframework.boot.web.servlet.support.SpringBootServletInitializer
7 |
8 | @SpringBootApplication
9 | class Application: SpringBootServletInitializer()
10 |
11 | fun main(args: Array) {
12 | SpringApplication.run(Application::class.java, *args)
13 | }
--------------------------------------------------------------------------------
/client/src/main/ng/src/app/feature/layout/lazy/lazy.module.ts:
--------------------------------------------------------------------------------
1 | import {RouterModule, Routes} from '@angular/router';
2 | import {NgModule} from '@angular/core';
3 | import {LazyComponent} from './lazy.component';
4 | import {SharedModule} from '../../../shared/shared.module';
5 |
6 | const routes: Routes = [
7 | {path: '', component: LazyComponent},
8 | ];
9 |
10 | @NgModule({
11 | imports: [SharedModule, RouterModule.forChild(routes)],
12 | exports: [LazyComponent],
13 | declarations: [LazyComponent]
14 | })
15 | export class LazyModule {}
16 |
--------------------------------------------------------------------------------
/client/src/main/ng/src/app/core/service/echo.service.ts:
--------------------------------------------------------------------------------
1 | import {Injectable} from '@angular/core';
2 | import {HttpClient} from '@angular/common/http';
3 | import {Observable} from 'rxjs';
4 | import {AbstractService} from './abstract-service';
5 | import {ApiConst} from '../../util/api.const';
6 |
7 | @Injectable({
8 | providedIn: 'root',
9 | })
10 | export class EchoService extends AbstractService {
11 |
12 | constructor(http: HttpClient) {
13 | super(http);
14 | }
15 |
16 | echo(): Observable {
17 | return this.text(ApiConst.ECHO);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/client/src/main/ng/src/app/feature/layout/layout.component.ts:
--------------------------------------------------------------------------------
1 | import {Component} from '@angular/core';
2 | import {AuthService} from '../../core/service/auth.service';
3 | import {Router} from '@angular/router';
4 |
5 | @Component({
6 | selector: 'app-layout',
7 | templateUrl: './layout.component.html'
8 | })
9 | export class LayoutComponent {
10 |
11 | constructor(
12 | private router: Router,
13 | private authService: AuthService
14 | ) {}
15 |
16 | logout() {
17 | this.authService.logout().subscribe(_ => {
18 | this.router.navigate(['/']);
19 | });
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/client/src/main/ng/src/app/feature/layout/layout.module.ts:
--------------------------------------------------------------------------------
1 | import {NgModule} from '@angular/core';
2 | import {SharedModule} from '../../shared/shared.module';
3 | import {LayoutComponent} from './layout.component';
4 | import {LayoutRouteModule} from './layout.route-module';
5 | import {HomeComponent} from './home/home.component';
6 |
7 | @NgModule({
8 | imports: [
9 | SharedModule,
10 | LayoutRouteModule,
11 | ],
12 | declarations: [
13 | HomeComponent,
14 | LayoutComponent
15 | ],
16 | exports: [
17 | LayoutComponent
18 | ]
19 | })
20 | export class LayoutModule {
21 | }
22 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.hiper2d
8 | spring-boot-angular-maven-starter
9 | 1.0-SNAPSHOT
10 | pom
11 |
12 |
13 | client
14 | server
15 |
16 |
--------------------------------------------------------------------------------
/server/src/main/kotlin/com/hiper2d/config/WebConfig.kt:
--------------------------------------------------------------------------------
1 | package com.hiper2d.config
2 |
3 | import org.springframework.context.annotation.Configuration
4 | import org.springframework.web.servlet.config.annotation.CorsRegistry
5 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
6 |
7 | @Configuration
8 | class WebConfig: WebMvcConfigurer {
9 |
10 | // todo: Understand why it is ignored. Probably some headers should be added to a client
11 | override fun addCorsMappings(registry: CorsRegistry) {
12 | registry.addMapping("/api/**")
13 | .allowedOrigins("http://localhost:9002")
14 | }
15 | }
16 |
17 |
--------------------------------------------------------------------------------
/client/src/main/ng/src/app/app-routing.module.ts:
--------------------------------------------------------------------------------
1 | import {RouterModule, Routes} from '@angular/router';
2 | import {NgModule} from '@angular/core';
3 | import {PageNotFoundComponent} from './feature/page-not-found/page-not-found.component';
4 | import {LoginComponent} from './feature/login/login.component';
5 |
6 | const routes: Routes = [
7 | {path: 'login', component: LoginComponent},
8 | {path: '**', component: PageNotFoundComponent},
9 | ];
10 |
11 | @NgModule({
12 | imports: [
13 | RouterModule.forRoot(routes, {useHash: true})
14 | ],
15 | exports: [
16 | RouterModule
17 | ]
18 | })
19 | export class AppRoutingModule { }
20 |
--------------------------------------------------------------------------------
/client/src/main/ng/src/app/core/core.module.ts:
--------------------------------------------------------------------------------
1 | import {NgModule} from '@angular/core';
2 | import {HTTP_INTERCEPTORS, HttpClientModule} from '@angular/common/http';
3 | import {AuthInterceptor} from './interceptor/auth.interceptor';
4 | import {XhrInterceptor} from './interceptor/xhr.interceptor';
5 |
6 | const httpInterceptorProviders = [
7 | {provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true},
8 | {provide: HTTP_INTERCEPTORS, useClass: XhrInterceptor, multi: true},
9 | ];
10 |
11 | @NgModule({
12 | imports: [
13 | HttpClientModule
14 | ],
15 | providers: [
16 | httpInterceptorProviders
17 | ]
18 | })
19 | export class CoreModule {
20 | }
21 |
--------------------------------------------------------------------------------
/client/src/main/ng/src/app/core/service/abstract-service.ts:
--------------------------------------------------------------------------------
1 | import {HttpClient, HttpHeaders, HttpParams} from '@angular/common/http';
2 | import {Observable} from 'rxjs/internal/Observable';
3 |
4 | export abstract class AbstractService {
5 |
6 | protected constructor(private http: HttpClient) {}
7 |
8 | protected post(url: string, body: T, params?: HttpParams): Observable {
9 | return this.http.post(url, body, {params: params});
10 | }
11 |
12 | protected textWithHeaders(url: string, headers: HttpHeaders): Observable {
13 | return this.http.get(url, {headers: headers, responseType: 'text'});
14 | }
15 |
16 | protected text(url: string) {
17 | return this.http.get(url, {responseType: 'text'});
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/client/src/main/ng/src/app/core/guard/auth.guard.ts:
--------------------------------------------------------------------------------
1 | import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot} from '@angular/router';
2 | import {Injectable} from '@angular/core';
3 | import {Observable} from 'rxjs/internal/Observable';
4 | import {AuthService} from '../service/auth.service';
5 |
6 | @Injectable({
7 | providedIn: 'root'
8 | })
9 | export class AuthGuard implements CanActivate {
10 |
11 | constructor(
12 | private authService: AuthService,
13 | private router: Router
14 | ) {}
15 |
16 | canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable | Promise | boolean {
17 | if (!this.authService.authenticated) {
18 | this.router.navigate(['/login']);
19 | }
20 | return true;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/client/src/main/ng/src/app/feature/layout/home/home.component.ts:
--------------------------------------------------------------------------------
1 | import {Component, OnInit} from '@angular/core';
2 | import {EchoService} from '../../../core/service/echo.service';
3 | import {AuthService} from '../../../core/service/auth.service';
4 |
5 | @Component({
6 | selector: 'app-home',
7 | templateUrl: './home.component.html'
8 | })
9 | export class HomeComponent implements OnInit {
10 | echo = 'nope';
11 | username = '';
12 |
13 | constructor(
14 | private echoService: EchoService,
15 | private authService: AuthService
16 | ) {
17 | }
18 |
19 | ngOnInit(): void {
20 | this.username = this.authService.username;
21 | this.echoService.echo().subscribe(result => {
22 | this.echo = result;
23 | });
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/client/src/main/ng/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import {NgModule} from '@angular/core';
2 | import {BrowserModule} from '@angular/platform-browser';
3 | import {AppRoutingModule} from './app-routing.module';
4 | import {AppComponent} from './app.component';
5 | import {CoreModule} from './core/core.module';
6 | import {LayoutModule} from './feature/layout/layout.module';
7 | import {PageNotFoundComponent} from './feature/page-not-found/page-not-found.component';
8 | import {LoginModule} from './feature/login/login.module';
9 |
10 | @NgModule({
11 | declarations: [
12 | AppComponent,
13 | PageNotFoundComponent
14 | ],
15 | imports: [
16 | BrowserModule,
17 | CoreModule,
18 | LayoutModule,
19 | LoginModule,
20 | AppRoutingModule
21 | ],
22 | bootstrap: [
23 | AppComponent
24 | ]
25 | })
26 |
27 | export class AppModule {
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/client/src/main/ng/protractor.conf.js:
--------------------------------------------------------------------------------
1 | // Protractor configuration file, see link for more information
2 | // https://github.com/angular/protractor/blob/master/lib/config.ts
3 |
4 | const { SpecReporter } = require('jasmine-spec-reporter');
5 |
6 | exports.config = {
7 | allScriptsTimeout: 11000,
8 | specs: [
9 | './e2e/**/*.e2e-spec.ts'
10 | ],
11 | capabilities: {
12 | 'browserName': 'chrome'
13 | },
14 | directConnect: true,
15 | baseUrl: 'http://localhost:4200/',
16 | framework: 'jasmine',
17 | jasmineNodeOpts: {
18 | showColors: true,
19 | defaultTimeoutInterval: 30000,
20 | print: function() {}
21 | },
22 | onPrepare() {
23 | require('ts-node').register({
24 | project: 'e2e/tsconfig.e2e.json'
25 | });
26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
27 | }
28 | };
29 |
--------------------------------------------------------------------------------
/client/src/main/ng/src/app/feature/layout/layout.route-module.ts:
--------------------------------------------------------------------------------
1 | import {RouterModule, Routes} from '@angular/router';
2 | import {HomeComponent} from './home/home.component';
3 | import {NgModule} from '@angular/core';
4 | import {AuthGuard} from '../../core/guard/auth.guard';
5 | import {LayoutComponent} from './layout.component';
6 |
7 | const routes: Routes = [
8 | {
9 | path: '', component: LayoutComponent,
10 | children: [
11 | { path: '', redirectTo: '/home', pathMatch: 'full', canActivate: [AuthGuard] },
12 | { path: 'home', component: HomeComponent, canActivate: [AuthGuard] },
13 | { path: 'lazy', loadChildren: './lazy/lazy.module#LazyModule', canActivate: [AuthGuard] }
14 | ]
15 | }
16 | ];
17 |
18 | @NgModule({
19 | imports: [
20 | RouterModule.forChild(routes)
21 | ],
22 | exports: [
23 | RouterModule
24 | ]
25 | })
26 | export class LayoutRouteModule {
27 | }
28 |
--------------------------------------------------------------------------------
/server/src/main/kotlin/com/hiper2d/controller/UserController.kt:
--------------------------------------------------------------------------------
1 | package com.hiper2d.controller
2 |
3 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
4 | import org.springframework.security.core.context.SecurityContextHolder
5 | import org.springframework.web.bind.annotation.GetMapping
6 | import org.springframework.web.bind.annotation.PostMapping
7 | import org.springframework.web.bind.annotation.RequestMapping
8 | import org.springframework.web.bind.annotation.RestController
9 |
10 | @RestController
11 | @RequestMapping("/api/user")
12 | class UserController {
13 |
14 | @GetMapping
15 | fun getUser(): String? {
16 | val auth = SecurityContextHolder.getContext().authentication
17 | return if (auth is UsernamePasswordAuthenticationToken) {
18 | auth.principal as String
19 | } else {
20 | null
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/client/src/main/ng/src/app/feature/login/login.component.scss:
--------------------------------------------------------------------------------
1 | $element-height: 34px;
2 | $element-margin-bottom: 16px;
3 | $element-width: 400px;
4 |
5 | :host {
6 | left: 50%;
7 | margin: (-($element-height*3 + $element-margin-bottom*2)/2) 0 0 (-$element-width/2);
8 | position: absolute;
9 | top: 50%;
10 | width: $element-width;
11 | }
12 |
13 | button {
14 | background-color: #636262;
15 | border: none;
16 | color: #fff;
17 | cursor: pointer;
18 | font-size: 14px;
19 | height: $element-height;
20 | line-height: $element-height;
21 | width: 100%;
22 | }
23 |
24 | button:disabled {
25 | background-color: #bdbbbb;
26 | cursor: default;
27 | }
28 |
29 | input {
30 | box-sizing: border-box;
31 | display: inline-block;
32 | font-size: 14px;
33 | height: $element-height;
34 | line-height: $element-height;
35 | margin-bottom: $element-margin-bottom;
36 | padding: 0 10px;
37 | width: 100%;
38 | }
39 |
--------------------------------------------------------------------------------
/server/src/main/kotlin/com/hiper2d/security/provider/AnyAuthenticationProvider.kt:
--------------------------------------------------------------------------------
1 | package com.hiper2d.security.provider
2 |
3 | import org.springframework.security.authentication.AuthenticationProvider
4 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
5 | import org.springframework.security.core.Authentication
6 | import org.springframework.stereotype.Component
7 |
8 | @Component
9 | class AnyAuthenticationProvider: AuthenticationProvider {
10 |
11 | override fun authenticate(token: Authentication?): Authentication? {
12 | val name: String? = token?.name
13 | val pass: String? = token?.credentials.toString()
14 | return if (name != null && pass != null) {
15 | UsernamePasswordAuthenticationToken(name, pass, emptyList())
16 | } else {
17 | null
18 | }
19 | }
20 |
21 | override fun supports(clazz: Class<*>?): Boolean {
22 | return true
23 | }
24 | }
--------------------------------------------------------------------------------
/client/src/main/ng/src/app/feature/login/login.component.ts:
--------------------------------------------------------------------------------
1 | import {Component} from '@angular/core';
2 | import {AuthService} from '../../core/service/auth.service';
3 | import {FormBuilder, FormGroup} from '@angular/forms';
4 | import {debounceTime} from 'rxjs/operators';
5 | import {Router} from '@angular/router';
6 |
7 | @Component({
8 | selector: 'app-login',
9 | templateUrl: './login.component.html',
10 | styleUrls: ['./login.component.scss']
11 | })
12 | export class LoginComponent {
13 |
14 | form: FormGroup;
15 |
16 | constructor(
17 | formBuilder: FormBuilder,
18 | private router: Router,
19 | private authService: AuthService
20 | ) {
21 | this.form = formBuilder.group({
22 | username: null,
23 | password: null
24 | });
25 | }
26 |
27 | login() {
28 | this.authService.authenticate(this.form.value).pipe(debounceTime(400)).subscribe(result => {
29 | if (result) {
30 | this.router.navigate(['/']);
31 | } else {
32 | this.form.reset();
33 | }
34 | });
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/client/src/main/ng/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage-istanbul-reporter'),
13 | require('@angular-devkit/build-angular/plugins/karma')
14 | ],
15 | client:{
16 | clearContext: false // leave Jasmine Spec Runner output visible in browser
17 | },
18 | coverageIstanbulReporter: {
19 | dir: require('path').join(__dirname, 'coverage'), reports: [ 'html', 'lcovonly' ],
20 | fixWebpackSourcePaths: true
21 | },
22 | angularCli: {
23 | environment: 'dev'
24 | },
25 | reporters: ['progress', 'kjhtml'],
26 | port: 9876,
27 | colors: true,
28 | logLevel: config.LOG_INFO,
29 | autoWatch: true,
30 | browsers: ['Chrome'],
31 | singleRun: false
32 | });
33 | };
34 |
--------------------------------------------------------------------------------
/client/src/main/ng/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/dist/long-stack-trace-zone';
4 | import 'zone.js/dist/proxy.js';
5 | import 'zone.js/dist/sync-test';
6 | import 'zone.js/dist/jasmine-patch';
7 | import 'zone.js/dist/async-test';
8 | import 'zone.js/dist/fake-async-test';
9 | import { getTestBed } from '@angular/core/testing';
10 | import {
11 | BrowserDynamicTestingModule,
12 | platformBrowserDynamicTesting
13 | } from '@angular/platform-browser-dynamic/testing';
14 |
15 | // Unfortunately there's no typing for the `__karma__` variable. Just declare it as any.
16 | declare const __karma__: any;
17 | declare const require: any;
18 |
19 | // Prevent Karma from running prematurely.
20 | __karma__.loaded = function () {};
21 |
22 | // First, initialize the Angular testing environment.
23 | getTestBed().initTestEnvironment(
24 | BrowserDynamicTestingModule,
25 | platformBrowserDynamicTesting()
26 | );
27 | // Then we find all the tests.
28 | const context = require.context('./', true, /\.spec\.ts$/);
29 | // And load the modules.
30 | context.keys().map(context);
31 | // Finally, start Karma to run the tests.
32 | __karma__.start();
33 |
--------------------------------------------------------------------------------
/client/src/main/ng/src/app/core/interceptor/auth.interceptor.ts:
--------------------------------------------------------------------------------
1 | import {Injectable} from '@angular/core';
2 | import {HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
3 | import {Observable} from 'rxjs/internal/Observable';
4 | import {catchError} from 'rxjs/operators';
5 | import {Router} from '@angular/router';
6 | import {throwError} from 'rxjs/internal/observable/throwError';
7 | import {EMPTY} from 'rxjs/internal/observable/empty';
8 |
9 | @Injectable()
10 | export class AuthInterceptor implements HttpInterceptor {
11 |
12 | constructor(private router: Router) {
13 | }
14 |
15 | intercept(req: HttpRequest, next: HttpHandler): Observable> {
16 | return next.handle(req).pipe(
17 | catchError(err => this.handleError(err))
18 | );
19 | }
20 |
21 | private handleError(err: any): Observable {
22 | if (err instanceof HttpErrorResponse) {
23 | const resp = err as HttpErrorResponse;
24 | if (resp.status === 401) {
25 | return this.handleUnauthorizedError();
26 | }
27 | }
28 | return throwError(err);
29 | }
30 |
31 | private handleUnauthorizedError(): Observable {
32 | this.router.navigate(['/login']);
33 | return EMPTY;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/client/src/main/ng/src/assets/reset.scss:
--------------------------------------------------------------------------------
1 | html, body, div, span, applet, object, iframe,
2 | h1, h2, h3, h4, h5, h6, p, blockquote, pre,
3 | a, abbr, acronym, address, big, cite, code,
4 | del, dfn, em, img, ins, kbd, q, s, samp,
5 | small, strike, strong, sub, sup, tt, var,
6 | b, u, i, center,
7 | dl, dt, dd, ol, ul, li,
8 | fieldset, form, label, legend,
9 | table, caption, tbody, tfoot, thead, tr, th, td,
10 | article, aside, canvas, details, embed,
11 | figure, figcaption, footer, header,
12 | main, menu, nav, output, ruby, section, summary,
13 | time, mark, audio, video {
14 | margin: 0;
15 | padding: 0;
16 | border: 0;
17 | vertical-align: baseline;
18 | }
19 | /* HTML5 display-role reset for older browsers */
20 | article, aside, details, figcaption, figure,
21 | footer, header, main, menu, nav, section {
22 | display: block;
23 | }
24 | body {
25 | line-height: 1;
26 | }
27 | ol, ul {
28 | list-style: none;
29 | }
30 | blockquote, q {
31 | quotes: none;
32 | }
33 | blockquote:before, blockquote:after,
34 | q:before, q:after {
35 | content: '';
36 | content: none;
37 | }
38 |
39 | /* Don't kill focus outline for keyboard users: http://24ways.org/2009/dont-lose-your-focus */
40 | a:hover, a:active {
41 | outline: none;
42 | }
43 |
44 | table {
45 | border-collapse: collapse;
46 | border-spacing: 0;
47 | }
--------------------------------------------------------------------------------
/client/src/main/ng/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "spring-boot-angular-maven-starter",
3 | "version": "0.0.0",
4 | "license": "MIT",
5 | "scripts": {
6 | "ng": "ng",
7 | "start": "ng serve --proxy-config proxy.conf.json --port 9002",
8 | "prebuild": "rimraf dist",
9 | "build": "ng build --prod",
10 | "test": "ng test",
11 | "lint": "ng lint",
12 | "e2e": "ng e2e"
13 | },
14 | "private": true,
15 | "dependencies": {
16 | "@angular/animations": "^6.0.7",
17 | "@angular/common": "^6.0.7",
18 | "@angular/compiler": "^6.0.7",
19 | "@angular/core": "^6.0.7",
20 | "@angular/forms": "^6.0.7",
21 | "@angular/http": "^6.0.7",
22 | "@angular/platform-browser": "^6.0.7",
23 | "@angular/platform-browser-dynamic": "^6.0.7",
24 | "@angular/router": "^6.0.7",
25 | "core-js": "^2.5.7",
26 | "rxjs": "^6.2.1",
27 | "zone.js": "^0.8.26"
28 | },
29 | "devDependencies": {
30 | "@angular/compiler-cli": "^6.0.7",
31 | "@angular-devkit/build-angular": "^0.6.8",
32 | "@angular/cli": "^6.1.0-rc.0",
33 | "@angular/language-service": "^6.0.7",
34 | "@types/jasmine": "^2.8.8",
35 | "@types/jasminewd2": "^2.0.3",
36 | "@types/node": "^10.3.3",
37 | "codelyzer": "^4.3.0",
38 | "jasmine-core": "^3.1.0",
39 | "jasmine-spec-reporter": "~4.2.1",
40 | "karma": "^2.0.2",
41 | "karma-chrome-launcher": "~2.2.0",
42 | "karma-coverage-istanbul-reporter": "^2.0.1",
43 | "karma-jasmine": "~1.1.1",
44 | "karma-jasmine-html-reporter": "^1.1.0",
45 | "protractor": "~5.3.0",
46 | "ts-node": "^7.0.0",
47 | "tslint": "^5.10.0",
48 | "typescript": "~2.7.2"
49 | }
50 | }
--------------------------------------------------------------------------------
/client/src/main/ng/src/app/core/service/auth.service.ts:
--------------------------------------------------------------------------------
1 | import {Injectable} from '@angular/core';
2 | import {AbstractService} from './abstract-service';
3 | import {HttpClient, HttpHeaders} from '@angular/common/http';
4 | import {Credentials} from '../../model/credentials.model';
5 | import {Observable} from 'rxjs/internal/Observable';
6 | import {ApiConst} from '../../util/api.const';
7 | import {map, tap} from 'rxjs/operators';
8 |
9 | @Injectable({
10 | providedIn: 'root'
11 | })
12 | export class AuthService extends AbstractService {
13 |
14 | authenticated = false;
15 | username: string;
16 |
17 | constructor(http: HttpClient) {
18 | super(http);
19 | }
20 |
21 | private static createToken(credentials: Credentials) {
22 | return {
23 | authorization: 'Basic ' + btoa(credentials.username + ':' + credentials.password)
24 | };
25 | }
26 |
27 | // Currently the user is not kept neither in Local Storage nor in Cookies.
28 | // Thereby authorization information is erased after every page update.
29 | authenticate(credentials: Credentials): Observable {
30 | const headers = new HttpHeaders(credentials ? AuthService.createToken(credentials) : {});
31 | return this.textWithHeaders(ApiConst.USER, headers).pipe(
32 | tap(response => this.username = response),
33 | map(response => !!response),
34 | tap(isAuthenticated => this.authenticated = isAuthenticated)
35 | );
36 | }
37 |
38 | logout(): Observable {
39 | return this.post(ApiConst.LOGOUT, null).pipe(
40 | tap(_ => {
41 | this.username = null;
42 | this.authenticated = false;
43 | })
44 | );
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | spring-boot-angular-maven-starter
2 | =============
3 |
4 | [](http://kotlinlang.org) [](https://travis-ci.org/hiper2d/spring-boot-angular-maven-starter)
5 |
6 | An example of Spring Boot 2 and Angular 6 integration with the help of Maven, Yarn, Kotlin.
7 |
8 | Also includes `Spring Security Authentication` configured for a single page application client. The backend has no control over the frontend and only responds with 200/40x HTTP statuses. All login/logout redirects are performed on the client's side. Username and password are any Latin strings.
9 |
10 | ##### Client npm dependencies status:
11 |
12 | [](https://david-dm.org/hiper2d/spring-boot-angular-maven-starter?path=client/src/main/ng)
13 | [](https://david-dm.org/hiper2d/spring-boot-angular-maven-starter?path=client/src/main/ng&type=dev)
14 |
15 | Consists of [server](./server/) and [client](./client/) modules.
16 |
17 | In production mode, they both are compiled and built into a single WAR archive which can be deployed to a web application server or run directly as a standalone Java application.
18 |
19 | In development mode, you work with both modules separately. The server is built and run with the help of Maven from the 'server' directory. The client is operated via Yarn from the 'client/src/main/ng' directory.
20 |
21 | ### Build
22 | ##### Production mode
23 | ```bash
24 | # build both server and client
25 | mvn clean install -Pprod
26 | ```
27 | ##### Development mode
28 | ```bash
29 | # build the server module without the client jar dependency
30 | # (can be run from the root or from the 'server' directories)
31 | mvn clean install
32 |
33 | # install client's npm dependencies (it's necessary for the first build only)
34 | # navigate to the 'client/src/main/ng' directory and run the following command
35 | yarn install
36 | ```
37 | ### Run
38 | ##### Production mode
39 | ```bash
40 | # navigate to the 'server' directory and run the following command
41 | mvn spring-boot:run
42 | ```
43 | > Access UI App at [http://localhost:9001](http://localhost:9001)
44 | ##### Development mode
45 | ```bash
46 | # navigate to the 'server' directory and run the following command
47 | mvn spring-boot:run
48 |
49 | # navigate to the 'client/src/main/ng' directory and run the following command
50 | yarn start
51 | ```
52 | > Access UI App at [http://localhost:9002](http://localhost:9002)
53 |
--------------------------------------------------------------------------------
/server/src/main/kotlin/com/hiper2d/config/WebSecurityConfig.kt:
--------------------------------------------------------------------------------
1 | package com.hiper2d.config
2 |
3 | import com.hiper2d.security.provider.AnyAuthenticationProvider
4 | import org.springframework.context.annotation.Configuration
5 | import org.springframework.http.HttpStatus
6 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
7 | import org.springframework.security.config.annotation.web.builders.HttpSecurity
8 | import org.springframework.security.config.annotation.web.builders.WebSecurity
9 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
10 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
11 | import org.springframework.security.web.authentication.HttpStatusEntryPoint
12 | import org.springframework.security.web.csrf.CookieCsrfTokenRepository
13 | import org.springframework.security.web.util.matcher.AntPathRequestMatcher
14 |
15 | @Configuration
16 | @EnableWebSecurity
17 | class WebSecurityConfig(
18 | private val anyAuthenticationProvider: AnyAuthenticationProvider
19 | ): WebSecurityConfigurerAdapter() {
20 |
21 | override fun configure(auth: AuthenticationManagerBuilder) {
22 | // A custom provider which allows to authenticate with any Latin username/password
23 | auth.authenticationProvider(anyAuthenticationProvider)
24 | }
25 |
26 | override fun configure(http: HttpSecurity) {
27 | http
28 | .httpBasic()
29 | .and()
30 | .authorizeRequests()
31 | // Necessary when running the application with compiled client as static resources
32 | .antMatchers("/").permitAll()
33 | .anyRequest().authenticated()
34 | .and()
35 | .logout()
36 | .logoutRequestMatcher(AntPathRequestMatcher("/api/logout", "POST"))
37 | .and()
38 | .csrf()
39 | // Allow a JavaScript client to read the XSRF-TOKEN from cookie
40 | .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
41 | .and()
42 | // Use MVC CORS configuration
43 | .cors()
44 | .and()
45 | .exceptionHandling()
46 | // An AuthenticationEntryPoint that sends a generic HttpStatus as a response
47 | .authenticationEntryPoint(HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))
48 | }
49 |
50 | override fun configure(web: WebSecurity) {
51 | // Everything below is necessary when running the application with compiled client as static resources
52 | web.ignoring()
53 | .mvcMatchers("/favicon.ico")
54 | .mvcMatchers("/*.css")
55 | .mvcMatchers("/*.js")
56 | }
57 | }
--------------------------------------------------------------------------------
/client/src/main/ng/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes polyfills needed by Angular and is loaded before the app.
3 | * You can add your own extra polyfills to this file.
4 | *
5 | * This file is divided into 2 sections:
6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
8 | * file.
9 | *
10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/
22 | // import 'core-js/es6/symbol';
23 | // import 'core-js/es6/object';
24 | // import 'core-js/es6/function';
25 | // import 'core-js/es6/parse-int';
26 | // import 'core-js/es6/parse-float';
27 | // import 'core-js/es6/number';
28 | // import 'core-js/es6/math';
29 | // import 'core-js/es6/string';
30 | // import 'core-js/es6/date';
31 | // import 'core-js/es6/array';
32 | // import 'core-js/es6/regexp';
33 | // import 'core-js/es6/map';
34 | // import 'core-js/es6/weak-map';
35 | // import 'core-js/es6/set';
36 |
37 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */
38 | // import 'classlist.js'; // Run `npm install --save classlist.js`.
39 |
40 | /** IE10 and IE11 requires the following for the Reflect API. */
41 | // import 'core-js/es6/reflect';
42 |
43 |
44 | /** Evergreen browsers require these. **/
45 | // Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove.
46 | import 'core-js/es7/reflect';
47 |
48 |
49 | /**
50 | * Required to support Web Animations `@angular/platform-browser/animations`.
51 | * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation
52 | **/
53 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`.
54 |
55 |
56 |
57 | /***************************************************************************************************
58 | * Zone JS is required by Angular itself.
59 | */
60 | import 'zone.js/dist/zone'; // Included with Angular CLI.
61 |
62 |
63 |
64 | /***************************************************************************************************
65 | * APPLICATION IMPORTS
66 | */
67 |
68 | /**
69 | * Date, currency, decimal and percent pipes.
70 | * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10
71 | */
72 | // import 'intl'; // Run `npm install --save intl`.
73 | /**
74 | * Need to import at least one locale-data with intl.
75 | */
76 | // import 'intl/locale-data/jsonp/en';
77 |
--------------------------------------------------------------------------------
/client/src/main/ng/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rulesDirectory": [
3 | "node_modules/codelyzer"
4 | ],
5 | "rules": {
6 | "arrow-return-shorthand": true,
7 | "callable-types": true,
8 | "class-name": true,
9 | "comment-format": [
10 | true,
11 | "check-space"
12 | ],
13 | "curly": true,
14 | "eofline": true,
15 | "forin": true,
16 | "import-blacklist": [
17 | true,
18 | "rxjs/Rx"
19 | ],
20 | "import-spacing": true,
21 | "indent": [
22 | true,
23 | "spaces"
24 | ],
25 | "interface-over-type-literal": true,
26 | "label-position": true,
27 | "max-line-length": [
28 | true,
29 | 140
30 | ],
31 | "member-access": false,
32 | "member-ordering": [
33 | true,
34 | {
35 | "order": [
36 | "static-field",
37 | "instance-field",
38 | "static-method",
39 | "instance-method"
40 | ]
41 | }
42 | ],
43 | "no-arg": true,
44 | "no-bitwise": true,
45 | "no-console": [
46 | true,
47 | "debug",
48 | "info",
49 | "time",
50 | "timeEnd",
51 | "trace"
52 | ],
53 | "no-construct": true,
54 | "no-debugger": true,
55 | "no-duplicate-super": true,
56 | "no-empty": false,
57 | "no-empty-interface": true,
58 | "no-eval": true,
59 | "no-inferrable-types": [
60 | true,
61 | "ignore-params"
62 | ],
63 | "no-misused-new": true,
64 | "no-non-null-assertion": true,
65 | "no-shadowed-variable": true,
66 | "no-string-literal": false,
67 | "no-string-throw": true,
68 | "no-switch-case-fall-through": true,
69 | "no-trailing-whitespace": true,
70 | "no-unnecessary-initializer": true,
71 | "no-unused-expression": true,
72 | "no-use-before-declare": true,
73 | "no-var-keyword": true,
74 | "object-literal-sort-keys": false,
75 | "one-line": [
76 | true,
77 | "check-open-brace",
78 | "check-catch",
79 | "check-else",
80 | "check-whitespace"
81 | ],
82 | "prefer-const": true,
83 | "quotemark": [
84 | true,
85 | "single"
86 | ],
87 | "radix": true,
88 | "semicolon": [
89 | true,
90 | "always"
91 | ],
92 | "triple-equals": [
93 | true,
94 | "allow-null-check"
95 | ],
96 | "typedef-whitespace": [
97 | true,
98 | {
99 | "call-signature": "nospace",
100 | "index-signature": "nospace",
101 | "parameter": "nospace",
102 | "property-declaration": "nospace",
103 | "variable-declaration": "nospace"
104 | }
105 | ],
106 | "typeof-compare": true,
107 | "unified-signatures": true,
108 | "variable-name": false,
109 | "whitespace": [
110 | true,
111 | "check-branch",
112 | "check-decl",
113 | "check-operator",
114 | "check-separator",
115 | "check-type"
116 | ],
117 | "directive-selector": [
118 | true,
119 | "attribute",
120 | "app",
121 | "camelCase"
122 | ],
123 | "component-selector": [
124 | true,
125 | "element",
126 | "app",
127 | "kebab-case"
128 | ],
129 | "use-input-property-decorator": true,
130 | "use-output-property-decorator": true,
131 | "use-host-property-decorator": true,
132 | "no-input-rename": true,
133 | "no-output-rename": true,
134 | "use-life-cycle-interface": true,
135 | "use-pipe-transform-interface": true,
136 | "component-class-suffix": true,
137 | "directive-class-suffix": true,
138 | "invoke-injectable": true
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/client/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | client
8 | 1.0-SNAPSHOT
9 | jar
10 |
11 |
12 | com.hiper2d
13 | spring-boot-angular-maven-starter
14 | 1.0-SNAPSHOT
15 |
16 |
17 |
18 | 1.6
19 | v10.4.1
20 | v1.7.0
21 |
22 |
23 |
24 |
25 |
26 |
27 | com.github.eirslett
28 | frontend-maven-plugin
29 | ${frontend-maven-plugin-version}
30 |
31 | src/main/ng
32 |
33 |
34 |
35 | install node and npm
36 |
37 | install-node-and-yarn
38 |
39 | generate-resources
40 |
41 | ${node-version}
42 | ${yarn-version}
43 |
44 |
45 |
46 | yarn install
47 |
48 | yarn
49 |
50 | generate-resources
51 |
52 | install
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | prod
64 |
65 |
66 |
67 | ${basedir}/src/main/ng/dist
68 |
69 | **
70 |
71 | META-INF/resources
72 |
73 |
74 |
75 |
76 |
77 | com.github.eirslett
78 | frontend-maven-plugin
79 |
80 |
81 | yarn build
82 |
83 | yarn
84 |
85 |
86 | run
87 | build
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
--------------------------------------------------------------------------------
/client/src/main/ng/angular.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "version": 1,
4 | "newProjectRoot": "projects",
5 | "schematics": {
6 | "@schematics/angular:component": {
7 | "prefix": "app",
8 | "styleext": "scss"
9 | },
10 | "@schematics/angular:directive": {
11 | "prefix": "app"
12 | }
13 | },
14 | "projects": {
15 | "spring-boot-angular-maven-starter": {
16 | "root": "",
17 | "sourceRoot": "src",
18 | "projectType": "application",
19 | "architect": {
20 | "build": {
21 | "builder": "@angular-devkit/build-angular:browser",
22 | "options": {
23 | "outputPath": "dist",
24 | "index": "src/index.html",
25 | "main": "src/main.ts",
26 | "tsConfig": "src/tsconfig.app.json",
27 | "polyfills": "src/polyfills.ts",
28 | "assets": [
29 | {
30 | "glob": "favicon.ico",
31 | "input": "src/assets",
32 | "output": "/"
33 | }
34 | ],
35 | "styles": [
36 | "src/assets/global.scss",
37 | "src/assets/reset.scss"
38 | ],
39 | "scripts": []
40 | },
41 | "configurations": {
42 | "production": {
43 | "optimization": true,
44 | "outputHashing": "all",
45 | "sourceMap": false,
46 | "extractCss": true,
47 | "namedChunks": false,
48 | "aot": true,
49 | "extractLicenses": true,
50 | "vendorChunk": false,
51 | "buildOptimizer": true,
52 | "fileReplacements": [
53 | {
54 | "replace": "src/environments/environment.ts",
55 | "with": "src/environments/environment.prod.ts"
56 | }
57 | ]
58 | }
59 | }
60 | },
61 | "serve": {
62 | "builder": "@angular-devkit/build-angular:dev-server",
63 | "options": {
64 | "browserTarget": "spring-boot-angular-maven-starter:build"
65 | },
66 | "configurations": {
67 | "production": {
68 | "browserTarget": "spring-boot-angular-maven-starter:build:production"
69 | }
70 | }
71 | },
72 | "extract-i18n": {
73 | "builder": "@angular-devkit/build-angular:extract-i18n",
74 | "options": {
75 | "browserTarget": "spring-boot-angular-maven-starter:build"
76 | }
77 | },
78 | "test": {
79 | "builder": "@angular-devkit/build-angular:karma",
80 | "options": {
81 | "main": "src/test.ts",
82 | "karmaConfig": "./karma.conf.js",
83 | "polyfills": "src/polyfills.ts",
84 | "tsConfig": "src/tsconfig.spec.json",
85 | "scripts": [],
86 | "styles": [
87 | "src/assets/global.css",
88 | "src/assets/reset.css"
89 | ],
90 | "assets": [
91 | {
92 | "glob": "favicon.ico",
93 | "input": "src/assets",
94 | "output": "/"
95 | }
96 | ]
97 | }
98 | },
99 | "lint": {
100 | "builder": "@angular-devkit/build-angular:tslint",
101 | "options": {
102 | "tsConfig": [
103 | "src/tsconfig.app.json",
104 | "src/tsconfig.spec.json"
105 | ],
106 | "exclude": [
107 | "**/node_modules/**"
108 | ]
109 | }
110 | }
111 | }
112 | },
113 | "spring-boot-angular-maven-starter-e2e": {
114 | "root": "",
115 | "sourceRoot": "",
116 | "projectType": "application",
117 | "architect": {
118 | "e2e": {
119 | "builder": "@angular-devkit/build-angular:protractor",
120 | "options": {
121 | "protractorConfig": "./protractor.conf.js",
122 | "devServerTarget": "spring-boot-angular-maven-starter:serve"
123 | }
124 | },
125 | "lint": {
126 | "builder": "@angular-devkit/build-angular:tslint",
127 | "options": {
128 | "tsConfig": [
129 | "e2e/tsconfig.e2e.json"
130 | ],
131 | "exclude": [
132 | "**/node_modules/**"
133 | ]
134 | }
135 | }
136 | }
137 | }
138 | },
139 | "defaultProject": "spring-boot-angular-maven-starter"
140 | }
141 |
--------------------------------------------------------------------------------
/server/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | server
8 | 1.0-SNAPSHOT
9 | war
10 |
11 |
12 | com.hiper2d
13 | spring-boot-angular-maven-starter
14 | 1.0-SNAPSHOT
15 |
16 |
17 |
18 | 2.9.4.1
19 | 1.2.50
20 | true
21 | 1.8
22 | 1.8
23 | 3.1.0
24 | 2.0.3.RELEASE
25 | com.hiper2d.Application
26 |
27 |
28 |
29 |
30 |
31 | org.springframework.boot
32 | spring-boot-dependencies
33 | ${spring-boot-version}
34 | pom
35 | import
36 |
37 |
38 |
39 |
40 |
41 |
42 | spring-milestones
43 | Spring Milestones
44 | http://repo.spring.io/milestone
45 |
46 |
47 |
48 |
49 |
50 | org.springframework.boot
51 | spring-boot-starter-web
52 |
53 |
54 | org.springframework.boot
55 | spring-boot-starter-logging
56 |
57 |
58 |
59 |
60 |
61 | org.springframework.boot
62 | spring-boot-starter-security
63 |
64 |
65 |
66 | org.springframework.boot
67 | spring-boot-starter-log4j2
68 |
69 |
70 |
71 | org.jetbrains.kotlin
72 | kotlin-stdlib-jdk8
73 | ${kotlin.version}
74 |
75 |
76 |
77 | org.jetbrains.kotlin
78 | kotlin-reflect
79 | ${kotlin.version}
80 |
81 |
82 |
83 | com.fasterxml.jackson.module
84 | jackson-module-kotlin
85 | ${jackson-module-kotlin.version}
86 |
87 |
88 |
89 |
90 | ${project.basedir}/src/main/kotlin
91 |
92 |
93 |
94 | kotlin-maven-plugin
95 | org.jetbrains.kotlin
96 | ${kotlin.version}
97 |
98 |
99 | spring
100 |
101 | 1.8
102 |
103 |
104 |
105 | compile
106 | compile
107 |
108 | compile
109 |
110 |
111 |
112 | test-compile
113 | test-compile
114 |
115 | test-compile
116 |
117 |
118 |
119 |
120 |
121 | org.jetbrains.kotlin
122 | kotlin-maven-allopen
123 | ${kotlin.version}
124 |
125 |
126 |
127 |
128 |
129 | maven-war-plugin
130 | ${maven.war.plugin.version}
131 |
132 |
133 |
134 | org.springframework.boot
135 | spring-boot-maven-plugin
136 | ${spring-boot-version}
137 |
138 |
139 |
140 | repackage
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 | prod
151 |
152 |
153 | com.hiper2d
154 | client
155 | 1.0-SNAPSHOT
156 |
157 |
158 |
159 |
160 |
--------------------------------------------------------------------------------