diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..74fb87b98 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,45 @@ +# CVS +.git +.gitignore + +# Dockerfile +Dockerfile +Dockerfile-prod + +# compiled output +dist +tmp +sme +target + +# dependencies +node_modules +bower_components + +# IDEs and editors +.idea +.vscode +.project +.classpath +*.launch +.editorconfig +.settings/ +nbproject/ + +# misc +.sass-cache +connect.lock +coverage/* +libpeerconnection.log +npm-debug.log +testem.log +typings +README.md + +# e2e +e2e/*.js +e2e/*.map + +#System Files +.DS_Store +Thumbs.db diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..b5945391b --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# See http://help.github.com/ignore-files/ for more about ignoring files. + +# compiled output +/dist +/tmp +/out-tsc + +# dependencies +/node_modules + +### STS ### +.classpath +.factorypath +.project +.settings +.springBeans + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +nb-configuration.xml +nbproject/private/ +build/ +nbbuild/ +dist/ +nbdist/ +.nb-gradle/ + +# IDEs and editors +/.idea +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# IDE - VSCode +.vscode/* +#!.vscode/settings.json +# #!.vscode/tasks.json +# #!.vscode/launch.json +# #!.vscode/extensions.json + +# misc +/.sass-cache +/connect.lock +/coverage +/libpeerconnection.log +npm-debug.log +testem.log +/typings + +# e2e +/e2e/*.js +/e2e/*.map + +# System Files +.DS_Store +Thumbs.db +package-lock.json +target/ +!.mvn/wrapper/maven-wrapper.jar + + +### frontend ### +npm-debug.* +src/main/resources/static/ +/frontend/nbproject/private/ \ No newline at end of file diff --git a/README.md b/README.md index dc609211d..19a538a8d 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,13 @@ +# Angular Spring Boot JWT Starter + [![npm](https://img.shields.io/badge/demo-online-ed1c46.svg)](http://angular-spring-starter.fanjin.io/) [![StackShare](https://img.shields.io/badge/tech-stack-0690fa.svg?style=flat)](https://stackshare.io/bfwg/angular4-spring-boot-jwt-starter) [![Build Status](https://travis-ci.org/bfwg/angular-spring-starter.svg?branch=master)](https://travis-ci.org/bfwg/angular-spring-starter) [![Maintenance Status][status-image]][status-url] [![License MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/bfwg/angular-spring-jwt-starter/blob/master/LICENSE) -

- - Spring Boot and Angular 2 - -

+![http://angular-spring-starter.fanjin.io](https://cloud.githubusercontent.com/assets/12819525/26094670/b0f6f192-39cf-11e7-8048-ab710b3dc1d1.png) -# Angular Spring Boot JWT Starter > An Angular full stack starter kit featuring [Angular](https://angular.io), [Router](https://angular.io/docs/ts/latest/guide/router.html), [Forms](https://angular.io/docs/ts/latest/guide/forms.html), [Http](https://angular.io/docs/ts/latest/guide/server-communication.html), [Services](https://gist.github.com/gdi2290/634101fec1671ee12b3e#_follow_@AngularClass_on_twitter), @@ -18,14 +15,73 @@ [JSON Web Token](https://jwt.io/) > If you're looking to use Angular as your frontend implementation, please check out [springboot-jwt-starter](https://github.com/bfwg/springboot-jwt-starter) -> A Spring Boot token-based security starter kit featuring [AngularJS](https://angularjs.org/) and [Spring Boot](https://projects.spring.io/spring-boot/) ([JSON Web Token](https://jwt.io/)) -

- Springboot JWT Starter -

- -## Quick start -**Make sure you have Maven and Java 11 or greater** -**Make sure you also have NPM 6.12.0, Node 12.13.0 and angular-cli@9.1.3 globally installed** +> A Spring Boot token-based security starter kit featuring [Angular](https://angular.dev/) and [Spring Boot](https://projects.spring.io/spring-boot/) ([JSON Web Token](https://jwt.io/)) + +![Springboot JWT Starter](https://cloud.githubusercontent.com/assets/12819525/26290994/3895daca-3e60-11e7-9465-421e0b029343.png) + +## Table of Contents + +- [Quick start with docker](#quick-start-with-docker) + - [Recipe to install docker-compose on Linux](#recipe-to-install-docker-compose-on-linux) +- [Quick start without docker](#quick-start-without-docker) +- [Deployment](#deployment) + - [JSON Web Token](#json-web-token) + - [Importing the Project in IntelliJ IDEA](#importing-the-project-in-intellij-idea) + +## Quick start with docker + +You'll need docker and docker-compose installed. + +In order to install __docker__ and __docker-compose__ follow these: + +1. [docker](https://docs.docker.com/install) installation + +2. [docker compose](https://docs.docker.com/compose/install/) installation + + > on Linux check [docker-compose recipe](#recipe-to-install-docker-compose-on-linux) +3. check __docker__ and __docker-compose__ installation, issue the following command on a terminal or console: + +```bash +docker --version +Docker version 17.12.0-ce, build c97c6d6 # or similar to 17.1x.x +docker-compose --version +docker-compose version 1.18.0, build 8dd22a9 # or similar to 1.1x.x +``` + +```bash +# clone our repo +# --depth 1 removes all but one .git commit history +git clone --depth 1 https://github.com/bfwg/angular-spring-starter.git +# issue docker-compose to start services, these will start in background mode as denoted by the -d flag +docker-compose up -d +# if everything builds successfully you should see something similar to: +Successfully built 0017f554842f +Successfully tagged angular-spring-starter_angular:latest +Creating angular-spring-starter_springboot_1 ... done +Creating angular-spring-starter_angular_1 ... done +# the app will be available at localhost +# if you wish to see your newly created containers issue: +docker ps +# if you wish to have access to a running shell inside a running container you can issue: +docker exec -it sh +``` + +### Recipe to install docker-compose on Linux + +To install __docker-compose__ under a GNU/nix system, one could use the following enchantment: + +```bash +COMPOSE_VERSION=`git ls-remote https://github.com/docker/compose | grep refs/tags | grep -oP "[0-9]+\.[0-9][0-9]+\.[0-9]+$" | tail -n 1` # get latest docker-compose version +sudo sh -c "curl -L https://github.com/docker/compose/releases/download/${COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose" # download and extract to /usr/local/bin with appropriate permissions, this should be already in your $PATH +sudo chmod +x /usr/local/bin/docker-compose +sudo sh -c "curl -L https://raw.githubusercontent.com/docker/compose/${COMPOSE_VERSION}/contrib/completion/bash/docker-compose > /etc/bash_completion.d/docker-compose" # docker-compose bash completions ... why not ?! +``` + +## Quick start without docker + +__Make sure you have Maven and Java 11 or greater__ +__Make sure you also have NPM 6.12.0, Node 12.13.0 and angular-cli@9.1.3 globally installed__ + ```bash # clone our repo # --depth 1 removes all but one .git commit history @@ -42,7 +98,7 @@ npm install npm start # change directory to the repo's backend folder -cd ../server +cd ../backend # install the server dependencies with mvn mvn install @@ -50,16 +106,18 @@ mvn install # start the backend server mvn spring-boot:run -# the fronend angular app will be running on port 4200 +# the frontend angular app will be running on port 4200 # the spring-boot server will be running on port 8080 ``` There are two user accounts present to demonstrate the different levels of access to the endpoints in the API and the different authorization exceptions: -``` + +```bash Admin - admin:123 User - user:123 ``` + For more detailed configuration/documentation, please check out the [frontend][frontend-doc] and [server][server-doc] folder. ## Deployment @@ -80,7 +138,7 @@ npm install ng build # change directory to the repo's backend folder -cd ../server +cd ../backend # install the server dependencies with mvn mvn install @@ -89,18 +147,21 @@ mvn install mvn spring-boot:run # the app will be running on port 8080 + ``` + For more deployment related info checkout here: [DEPLOYMENT DOC](https://angular.io/docs/ts/latest/guide/deployment.html) ### JSON Web Token -> JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties. -for more info, check out https://jwt.io/ +> JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties. +> For more info, check out [https://jwt.io/](https://jwt.io/). > Token authentication is a more modern approach and is designed solve problems session IDs stored server-side can’t. Using tokens in place of session IDs can lower your server load, streamline permission management, and provide better tools for supporting a distributed or cloud-based infrastructure. > -> -- Stormpath +> -- *Stormpath* ### Importing the Project in IntelliJ IDEA + 1. Click "Import Project" on the launch screen 2. Select the projects root folder, then select "Import project from external model" and choose "Maven" 3. Tick the checkboxes "Import Maven projects automatically" and "Import projects recursively" @@ -112,21 +173,20 @@ for more info, check out https://jwt.io/ 9. You should now see both (frontend and backend) modules in the Project view ### Contributing + I'll accept pretty much everything so feel free to open a Pull-Request This project is inspired by + - [Stormpath](https://stormpath.com/blog/token-auth-spa) - [Cerberus](https://github.com/brahalla/Cerberus) - [jwt-spring-security-demo](https://github.com/szerhusenBC/jwt-spring-security-demo) -___ - ## License - [MIT](/LICENSE) + [MIT](/LICENSE) [frontend-doc]: https://github.com/bfwg/angular-spring-jwt-starter/tree/master/frontend [server-doc]: https://github.com/bfwg/angular-spring-jwt-starter/tree/master/server [status-image]: https://img.shields.io/badge/status-maintained-brightgreen.svg [status-url]: https://github.com/bfwg/angular-spring-jwt-starter - diff --git a/backend/.dockerignore b/backend/.dockerignore new file mode 100644 index 000000000..74fb87b98 --- /dev/null +++ b/backend/.dockerignore @@ -0,0 +1,45 @@ +# CVS +.git +.gitignore + +# Dockerfile +Dockerfile +Dockerfile-prod + +# compiled output +dist +tmp +sme +target + +# dependencies +node_modules +bower_components + +# IDEs and editors +.idea +.vscode +.project +.classpath +*.launch +.editorconfig +.settings/ +nbproject/ + +# misc +.sass-cache +connect.lock +coverage/* +libpeerconnection.log +npm-debug.log +testem.log +typings +README.md + +# e2e +e2e/*.js +e2e/*.map + +#System Files +.DS_Store +Thumbs.db diff --git a/server/.gitignore b/backend/.gitignore similarity index 93% rename from server/.gitignore rename to backend/.gitignore index caeac0ffd..154c0cf68 100644 --- a/server/.gitignore +++ b/backend/.gitignore @@ -15,6 +15,7 @@ target/ *.ipr ### NetBeans ### +nb-configuration.xml nbproject/private/ build/ nbbuild/ diff --git a/server/.mvn/wrapper/maven-wrapper.jar b/backend/.mvn/wrapper/maven-wrapper.jar similarity index 100% rename from server/.mvn/wrapper/maven-wrapper.jar rename to backend/.mvn/wrapper/maven-wrapper.jar diff --git a/server/.mvn/wrapper/maven-wrapper.properties b/backend/.mvn/wrapper/maven-wrapper.properties similarity index 100% rename from server/.mvn/wrapper/maven-wrapper.properties rename to backend/.mvn/wrapper/maven-wrapper.properties diff --git a/server/README.md b/backend/README.md similarity index 92% rename from server/README.md rename to backend/README.md index 62174bca6..ebf60b014 100644 --- a/server/README.md +++ b/backend/README.md @@ -1,11 +1,12 @@ # Angular Spring Boot JWT Starter + This sub-project is the backend server portion of the project. -**Make sure you have Maven and Java 1.8 or greater** +Make sure you have Maven and Java 1.8 or greater. ```bash -# change directory to server -cd angular-spring-starter/server +# change directory to backend +cd angular-spring-starter/backend # install the repo with mvn mvn install @@ -19,10 +20,10 @@ mvn spring-boot:run # - Admin - admin:123 ``` - ## File Structure -``` -angular-spring-starter/server + +```txt +angular-spring-starter/backend ├──src/ * our source files │ ├──main │ │ ├──java.com.bfwg @@ -58,7 +59,7 @@ angular-spring-starter/server │ │ │ │ └──UserService.java │ │ │ └──Application.java * Application main enterance │ │ └──recources - │ │ ├──static * Angular7 frontend code will get built and served from here. + │ │ ├──static * Angular frontend code will get built and served from here. │ │ ├──application.yml * application variables are configured here │ │ ├──banner.txt * application banner :^) │ │ └──import.sql * h2 database query(table creation) @@ -67,13 +68,13 @@ angular-spring-starter/server ``` ## Configuration -- **WebSecurityConfig.java**: The server-side authentication configurations. -- **application.yml**: Application level properties i.e the token expire time, token secret etc. You can find a reference of all application properties [here](http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html). -- **JWT token TTL**: JWT Tokens are configured to expire after 10 minutes, you can get a new token by signing in again. -- **Using a different database**: This Starter kit is using an embedded H2 database that is automatically configured by Spring Boot. If you want to connect to another database you have to specify the connection in the *application.yml* in the resource directory. Here is an example for a MySQL DB: +- __WebSecurityConfig.java__: The server-side authentication configurations. +- __application.yml__: Application level properties i.e the token expire time, token secret etc. You can find a reference of all application properties [here](http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html). +- __JWT token TTL__: JWT Tokens are configured to expire after 10 minutes, you can get a new token by signing in again. +- __Using a different database__: This Starter kit is using an embedded H2 database that is automatically configured by Spring Boot. If you want to connect to another database you have to specify the connection in the *application.yml* in the resource directory. Here is an example for a MySQL DB: -``` +```yaml spring: jpa: hibernate: @@ -85,7 +86,9 @@ spring: password: myPassword driver-class-name: com.mysql.jdbc.Driver ``` + *Hint: For other databases like MySQL sequences don't work for ID generation. So you have to change the GenerationType in the entity beans to 'AUTO' or 'IDENTITY'.* ### Generating password hash for users + I'm using [bcrypt](https://en.wikipedia.org/wiki/Bcrypt) to encode passwords. Your can generate your hashes with this simple tool: [BCrypt Calculator](https://www.dailycred.com/article/bcrypt-calculator) diff --git a/backend/dockerfile b/backend/dockerfile new file mode 100644 index 000000000..296f93eea --- /dev/null +++ b/backend/dockerfile @@ -0,0 +1,33 @@ +FROM maven:3-jdk-13-alpine + +#VOLUME /tmp + +ENV JAVA_OPTS "" + +ENV JAR_NAME "angular-spring-starter-0.1.2.jar" + +# RUN addgroup -S app && adduser -S -g app app && apk update && apk upgrade +RUN apk update && apk upgrade +# https://www.alpinelinux.org/posts/Docker-image-vulnerability-CVE-2019-5021.html +# https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-5021 +# make sure root login is disabled +RUN sed -i -e 's/^root::/root:!:/' /etc/shadow + +# USER app + +WORKDIR /home/app +# COPY target/*.jar app.jar +COPY . $WORKDIR + +RUN mvn clean verify + +# RUN mvn clean install && \ +# chown -R app:app /home/app && \ +# cp -v target/*.jar app.jar + +EXPOSE 8080 + +CMD java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar /home/app/target/${JAR_NAME} +# CMD ["java", "$JAVA_OPTS","-Djava.security.egd=file:/dev/./urandom -jar app.jar"] +# CMD [ "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar app.jar" ] +# ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar app.jar" ] diff --git a/server/mvnw b/backend/mvnw old mode 100755 new mode 100644 similarity index 100% rename from server/mvnw rename to backend/mvnw diff --git a/server/mvnw.cmd b/backend/mvnw.cmd similarity index 100% rename from server/mvnw.cmd rename to backend/mvnw.cmd diff --git a/server/pom.xml b/backend/pom.xml similarity index 84% rename from server/pom.xml rename to backend/pom.xml index 42c2e1a16..224021ec3 100644 --- a/server/pom.xml +++ b/backend/pom.xml @@ -1,6 +1,6 @@ - + 4.0.0 com.bfwg @@ -15,16 +15,21 @@ org.springframework.boot spring-boot-starter-parent 2.2.6.RELEASE - + + UTF-8 UTF-8 - 1.8 + 11 + org.springframework.boot spring-boot-starter-web @@ -72,11 +77,10 @@ io.rest-assured spring-mock-mvc - 3.0.5 + 3.0.7 test - diff --git a/server/src/main/java/com/bfwg/Application.java b/backend/src/main/java/com/bfwg/Application.java similarity index 100% rename from server/src/main/java/com/bfwg/Application.java rename to backend/src/main/java/com/bfwg/Application.java diff --git a/server/src/main/java/com/bfwg/config/WebSecurityConfig.java b/backend/src/main/java/com/bfwg/config/WebSecurityConfig.java similarity index 100% rename from server/src/main/java/com/bfwg/config/WebSecurityConfig.java rename to backend/src/main/java/com/bfwg/config/WebSecurityConfig.java diff --git a/server/src/main/java/com/bfwg/exception/ExceptionHandlingController.java b/backend/src/main/java/com/bfwg/exception/ExceptionHandlingController.java similarity index 97% rename from server/src/main/java/com/bfwg/exception/ExceptionHandlingController.java rename to backend/src/main/java/com/bfwg/exception/ExceptionHandlingController.java index 6a1089be1..0635a3afc 100644 --- a/server/src/main/java/com/bfwg/exception/ExceptionHandlingController.java +++ b/backend/src/main/java/com/bfwg/exception/ExceptionHandlingController.java @@ -1,18 +1,18 @@ -package com.bfwg.exception; - -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.ControllerAdvice; -import org.springframework.web.bind.annotation.ExceptionHandler; - -@ControllerAdvice -public class ExceptionHandlingController { - - @ExceptionHandler(ResourceConflictException.class) - public ResponseEntity resourceConflict(ResourceConflictException ex) { - ExceptionResponse response = new ExceptionResponse(); - response.setErrorCode("Conflict"); - response.setErrorMessage(ex.getMessage()); - return new ResponseEntity(response, HttpStatus.CONFLICT); - } -} +package com.bfwg.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; + +@ControllerAdvice +public class ExceptionHandlingController { + + @ExceptionHandler(ResourceConflictException.class) + public ResponseEntity resourceConflict(ResourceConflictException ex) { + ExceptionResponse response = new ExceptionResponse(); + response.setErrorCode("Conflict"); + response.setErrorMessage(ex.getMessage()); + return new ResponseEntity(response, HttpStatus.CONFLICT); + } +} diff --git a/server/src/main/java/com/bfwg/exception/ExceptionResponse.java b/backend/src/main/java/com/bfwg/exception/ExceptionResponse.java similarity index 95% rename from server/src/main/java/com/bfwg/exception/ExceptionResponse.java rename to backend/src/main/java/com/bfwg/exception/ExceptionResponse.java index ad6e978a1..cf20b3617 100644 --- a/server/src/main/java/com/bfwg/exception/ExceptionResponse.java +++ b/backend/src/main/java/com/bfwg/exception/ExceptionResponse.java @@ -1,26 +1,26 @@ -package com.bfwg.exception; - -public class ExceptionResponse { - - private String errorCode; - private String errorMessage; - - public ExceptionResponse() { - } - - public String getErrorCode() { - return errorCode; - } - - public void setErrorCode(String errorCode) { - this.errorCode = errorCode; - } - - public String getErrorMessage() { - return errorMessage; - } - - public void setErrorMessage(String errorMessage) { - this.errorMessage = errorMessage; - } -} +package com.bfwg.exception; + +public class ExceptionResponse { + + private String errorCode; + private String errorMessage; + + public ExceptionResponse() { + } + + public String getErrorCode() { + return errorCode; + } + + public void setErrorCode(String errorCode) { + this.errorCode = errorCode; + } + + public String getErrorMessage() { + return errorMessage; + } + + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + } +} diff --git a/server/src/main/java/com/bfwg/exception/ResourceConflictException.java b/backend/src/main/java/com/bfwg/exception/ResourceConflictException.java similarity index 96% rename from server/src/main/java/com/bfwg/exception/ResourceConflictException.java rename to backend/src/main/java/com/bfwg/exception/ResourceConflictException.java index 827a28f12..8ca822067 100644 --- a/server/src/main/java/com/bfwg/exception/ResourceConflictException.java +++ b/backend/src/main/java/com/bfwg/exception/ResourceConflictException.java @@ -1,20 +1,20 @@ -package com.bfwg.exception; - -public class ResourceConflictException extends RuntimeException { - - private static final long serialVersionUID = 1791564636123821405L; - private Long resourceId; - - public ResourceConflictException(Long resourceId, String message) { - super(message); - this.setResourceId(resourceId); - } - - public Long getResourceId() { - return resourceId; - } - - public void setResourceId(Long resourceId) { - this.resourceId = resourceId; - } -} +package com.bfwg.exception; + +public class ResourceConflictException extends RuntimeException { + + private static final long serialVersionUID = 1791564636123821405L; + private Long resourceId; + + public ResourceConflictException(Long resourceId, String message) { + super(message); + this.setResourceId(resourceId); + } + + public Long getResourceId() { + return resourceId; + } + + public void setResourceId(Long resourceId) { + this.resourceId = resourceId; + } +} diff --git a/server/src/main/java/com/bfwg/model/Authority.java b/backend/src/main/java/com/bfwg/model/Authority.java similarity index 100% rename from server/src/main/java/com/bfwg/model/Authority.java rename to backend/src/main/java/com/bfwg/model/Authority.java diff --git a/server/src/main/java/com/bfwg/model/User.java b/backend/src/main/java/com/bfwg/model/User.java similarity index 100% rename from server/src/main/java/com/bfwg/model/User.java rename to backend/src/main/java/com/bfwg/model/User.java diff --git a/server/src/main/java/com/bfwg/model/UserRequest.java b/backend/src/main/java/com/bfwg/model/UserRequest.java similarity index 100% rename from server/src/main/java/com/bfwg/model/UserRequest.java rename to backend/src/main/java/com/bfwg/model/UserRequest.java diff --git a/server/src/main/java/com/bfwg/model/UserRoleName.java b/backend/src/main/java/com/bfwg/model/UserRoleName.java similarity index 100% rename from server/src/main/java/com/bfwg/model/UserRoleName.java rename to backend/src/main/java/com/bfwg/model/UserRoleName.java diff --git a/server/src/main/java/com/bfwg/model/UserTokenState.java b/backend/src/main/java/com/bfwg/model/UserTokenState.java similarity index 100% rename from server/src/main/java/com/bfwg/model/UserTokenState.java rename to backend/src/main/java/com/bfwg/model/UserTokenState.java diff --git a/server/src/main/java/com/bfwg/repository/AuthorityRepository.java b/backend/src/main/java/com/bfwg/repository/AuthorityRepository.java similarity index 96% rename from server/src/main/java/com/bfwg/repository/AuthorityRepository.java rename to backend/src/main/java/com/bfwg/repository/AuthorityRepository.java index b73f4201e..c56d86d09 100644 --- a/server/src/main/java/com/bfwg/repository/AuthorityRepository.java +++ b/backend/src/main/java/com/bfwg/repository/AuthorityRepository.java @@ -1,9 +1,9 @@ -package com.bfwg.repository; - -import com.bfwg.model.Authority; -import com.bfwg.model.UserRoleName; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface AuthorityRepository extends JpaRepository { - Authority findByName(UserRoleName name); -} +package com.bfwg.repository; + +import com.bfwg.model.Authority; +import com.bfwg.model.UserRoleName; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface AuthorityRepository extends JpaRepository { + Authority findByName(UserRoleName name); +} diff --git a/server/src/main/java/com/bfwg/repository/UserRepository.java b/backend/src/main/java/com/bfwg/repository/UserRepository.java similarity index 100% rename from server/src/main/java/com/bfwg/repository/UserRepository.java rename to backend/src/main/java/com/bfwg/repository/UserRepository.java diff --git a/server/src/main/java/com/bfwg/rest/AuthenticationController.java b/backend/src/main/java/com/bfwg/rest/AuthenticationController.java similarity index 96% rename from server/src/main/java/com/bfwg/rest/AuthenticationController.java rename to backend/src/main/java/com/bfwg/rest/AuthenticationController.java index 1cf3b288c..cbb1e836b 100644 --- a/server/src/main/java/com/bfwg/rest/AuthenticationController.java +++ b/backend/src/main/java/com/bfwg/rest/AuthenticationController.java @@ -8,6 +8,7 @@ import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @@ -23,6 +24,7 @@ * Created by fan.jin on 2017-05-10. */ +@CrossOrigin(origins = "*", maxAge = 3600) @RestController @RequestMapping(value = "/api", produces = MediaType.APPLICATION_JSON_VALUE) public class AuthenticationController { diff --git a/server/src/main/java/com/bfwg/rest/PublicController.java b/backend/src/main/java/com/bfwg/rest/PublicController.java similarity index 87% rename from server/src/main/java/com/bfwg/rest/PublicController.java rename to backend/src/main/java/com/bfwg/rest/PublicController.java index e549bbbbd..52ec96f7e 100644 --- a/server/src/main/java/com/bfwg/rest/PublicController.java +++ b/backend/src/main/java/com/bfwg/rest/PublicController.java @@ -1,6 +1,7 @@ package com.bfwg.rest; import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -13,6 +14,7 @@ * Created by fan.jin on 2017-05-08. */ +@CrossOrigin(origins = "*", maxAge = 3600) @RestController @RequestMapping(value = "/api", produces = MediaType.APPLICATION_JSON_VALUE) public class PublicController { diff --git a/server/src/main/java/com/bfwg/rest/UserController.java b/backend/src/main/java/com/bfwg/rest/UserController.java similarity index 96% rename from server/src/main/java/com/bfwg/rest/UserController.java rename to backend/src/main/java/com/bfwg/rest/UserController.java index 90ac3b5d2..64bfc7522 100644 --- a/server/src/main/java/com/bfwg/rest/UserController.java +++ b/backend/src/main/java/com/bfwg/rest/UserController.java @@ -11,6 +11,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -28,6 +29,7 @@ * Created by fan.jin on 2016-10-15. */ +@CrossOrigin(origins = "*", maxAge = 3600) @RestController @RequestMapping(value = "/api", produces = MediaType.APPLICATION_JSON_VALUE) public class UserController { diff --git a/server/src/main/java/com/bfwg/security/TokenHelper.java b/backend/src/main/java/com/bfwg/security/TokenHelper.java similarity index 86% rename from server/src/main/java/com/bfwg/security/TokenHelper.java rename to backend/src/main/java/com/bfwg/security/TokenHelper.java index dbc93c9c7..358bae69e 100644 --- a/server/src/main/java/com/bfwg/security/TokenHelper.java +++ b/backend/src/main/java/com/bfwg/security/TokenHelper.java @@ -1,13 +1,16 @@ package com.bfwg.security; import io.jsonwebtoken.Claims; +import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.MalformedJwtException; import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.SignatureException; +import io.jsonwebtoken.UnsupportedJwtException; import org.joda.time.DateTime; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; -import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.stereotype.Component; @@ -15,12 +18,12 @@ import javax.servlet.http.HttpServletRequest; import java.util.Date; import java.util.Map; - +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UsernameNotFoundException; /** * Created by fan.jin on 2016-10-19. */ - @Component public class TokenHelper { @@ -67,7 +70,8 @@ private Claims getClaimsFromToken(String token) { .setSigningKey(this.SECRET) .parseClaimsJws(token) .getBody(); - } catch (Exception e) { + } catch (ExpiredJwtException | MalformedJwtException | SignatureException | UnsupportedJwtException + | IllegalArgumentException e) { claims = null; } return claims; @@ -87,7 +91,7 @@ public Boolean canTokenBeRefreshed(String token) { String username = getUsernameFromToken(token); UserDetails userDetails = userDetailsService.loadUserByUsername(username); return expirationDate.compareTo(generateCurrentDate()) > 0; - } catch (Exception e) { + } catch (UsernameNotFoundException e) { return false; } } @@ -119,15 +123,14 @@ private Date generateExpirationDate() { public String getToken(HttpServletRequest request) { /** - * Getting the token from Cookie store + * Getting the token from Cookie store */ Cookie authCookie = getCookieValueByName(request, AUTH_COOKIE); if (authCookie != null) { return authCookie.getValue(); } /** - * Getting the token from Authentication header - * e.g Bearer your_token + * Getting the token from Authentication header e.g Bearer your_token */ String authHeader = request.getHeader(AUTH_HEADER); if (authHeader != null && authHeader.startsWith("Bearer ")) { @@ -148,9 +151,9 @@ public Cookie getCookieValueByName(HttpServletRequest request, String name) { if (request.getCookies() == null) { return null; } - for (int i = 0; i < request.getCookies().length; i++) { - if (request.getCookies()[i].getName().equals(name)) { - return request.getCookies()[i]; + for (Cookie cookie : request.getCookies()) { + if (cookie.getName().equals(name)) { + return cookie; } } return null; diff --git a/server/src/main/java/com/bfwg/security/auth/AnonAuthentication.java b/backend/src/main/java/com/bfwg/security/auth/AnonAuthentication.java similarity index 100% rename from server/src/main/java/com/bfwg/security/auth/AnonAuthentication.java rename to backend/src/main/java/com/bfwg/security/auth/AnonAuthentication.java diff --git a/server/src/main/java/com/bfwg/security/auth/AuthenticationFailureHandler.java b/backend/src/main/java/com/bfwg/security/auth/AuthenticationFailureHandler.java similarity index 100% rename from server/src/main/java/com/bfwg/security/auth/AuthenticationFailureHandler.java rename to backend/src/main/java/com/bfwg/security/auth/AuthenticationFailureHandler.java diff --git a/server/src/main/java/com/bfwg/security/auth/AuthenticationSuccessHandler.java b/backend/src/main/java/com/bfwg/security/auth/AuthenticationSuccessHandler.java similarity index 100% rename from server/src/main/java/com/bfwg/security/auth/AuthenticationSuccessHandler.java rename to backend/src/main/java/com/bfwg/security/auth/AuthenticationSuccessHandler.java diff --git a/server/src/main/java/com/bfwg/security/auth/LogoutSuccess.java b/backend/src/main/java/com/bfwg/security/auth/LogoutSuccess.java similarity index 100% rename from server/src/main/java/com/bfwg/security/auth/LogoutSuccess.java rename to backend/src/main/java/com/bfwg/security/auth/LogoutSuccess.java diff --git a/server/src/main/java/com/bfwg/security/auth/RestAuthenticationEntryPoint.java b/backend/src/main/java/com/bfwg/security/auth/RestAuthenticationEntryPoint.java similarity index 100% rename from server/src/main/java/com/bfwg/security/auth/RestAuthenticationEntryPoint.java rename to backend/src/main/java/com/bfwg/security/auth/RestAuthenticationEntryPoint.java diff --git a/server/src/main/java/com/bfwg/security/auth/TokenAuthenticationFilter.java b/backend/src/main/java/com/bfwg/security/auth/TokenAuthenticationFilter.java similarity index 99% rename from server/src/main/java/com/bfwg/security/auth/TokenAuthenticationFilter.java rename to backend/src/main/java/com/bfwg/security/auth/TokenAuthenticationFilter.java index e136e1022..463947d75 100644 --- a/server/src/main/java/com/bfwg/security/auth/TokenAuthenticationFilter.java +++ b/backend/src/main/java/com/bfwg/security/auth/TokenAuthenticationFilter.java @@ -76,6 +76,7 @@ public void doFilterInternal(HttpServletRequest request, HttpServletResponse res authentication.setToken(authToken); SecurityContextHolder.getContext().setAuthentication(authentication); } catch (Exception e) { + logger.error(e); SecurityContextHolder.getContext().setAuthentication(new AnonAuthentication()); } } else { diff --git a/server/src/main/java/com/bfwg/security/auth/TokenBasedAuthentication.java b/backend/src/main/java/com/bfwg/security/auth/TokenBasedAuthentication.java similarity index 100% rename from server/src/main/java/com/bfwg/security/auth/TokenBasedAuthentication.java rename to backend/src/main/java/com/bfwg/security/auth/TokenBasedAuthentication.java diff --git a/server/src/main/java/com/bfwg/service/AuthorityService.java b/backend/src/main/java/com/bfwg/service/AuthorityService.java similarity index 94% rename from server/src/main/java/com/bfwg/service/AuthorityService.java rename to backend/src/main/java/com/bfwg/service/AuthorityService.java index 09e09a5ed..47e489ea4 100644 --- a/server/src/main/java/com/bfwg/service/AuthorityService.java +++ b/backend/src/main/java/com/bfwg/service/AuthorityService.java @@ -1,16 +1,16 @@ -package com.bfwg.service; - -import com.bfwg.model.Authority; -import com.bfwg.model.UserRoleName; - -import java.util.List; - -/** - * Created by fan.jin on 2016-11-07. - */ -public interface AuthorityService { - List findById(Long id); - - List findByName(UserRoleName name); - -} +package com.bfwg.service; + +import com.bfwg.model.Authority; +import com.bfwg.model.UserRoleName; + +import java.util.List; + +/** + * Created by fan.jin on 2016-11-07. + */ +public interface AuthorityService { + List findById(Long id); + + List findByName(UserRoleName name); + +} diff --git a/server/src/main/java/com/bfwg/service/UserService.java b/backend/src/main/java/com/bfwg/service/UserService.java similarity index 100% rename from server/src/main/java/com/bfwg/service/UserService.java rename to backend/src/main/java/com/bfwg/service/UserService.java diff --git a/server/src/main/java/com/bfwg/service/impl/AuthorityServiceImpl.java b/backend/src/main/java/com/bfwg/service/impl/AuthorityServiceImpl.java similarity index 92% rename from server/src/main/java/com/bfwg/service/impl/AuthorityServiceImpl.java rename to backend/src/main/java/com/bfwg/service/impl/AuthorityServiceImpl.java index 1275885f9..94fdd3f6b 100644 --- a/server/src/main/java/com/bfwg/service/impl/AuthorityServiceImpl.java +++ b/backend/src/main/java/com/bfwg/service/impl/AuthorityServiceImpl.java @@ -22,7 +22,6 @@ public AuthorityServiceImpl(AuthorityRepository authorityRepository) { @Override public List findById(Long id) { - // TODO Auto-generated method stub Authority auth = this.authorityRepository.getOne(id); List auths = new ArrayList<>(); @@ -32,7 +31,7 @@ public List findById(Long id) { @Override public List findByName(UserRoleName name) { - // TODO Auto-generated method stub + Authority auth = this.authorityRepository.findByName(name); List auths = new ArrayList<>(); auths.add(auth); diff --git a/server/src/main/java/com/bfwg/service/impl/CustomUserDetailsService.java b/backend/src/main/java/com/bfwg/service/impl/CustomUserDetailsService.java similarity index 100% rename from server/src/main/java/com/bfwg/service/impl/CustomUserDetailsService.java rename to backend/src/main/java/com/bfwg/service/impl/CustomUserDetailsService.java diff --git a/server/src/main/java/com/bfwg/service/impl/UserServiceImpl.java b/backend/src/main/java/com/bfwg/service/impl/UserServiceImpl.java similarity index 97% rename from server/src/main/java/com/bfwg/service/impl/UserServiceImpl.java rename to backend/src/main/java/com/bfwg/service/impl/UserServiceImpl.java index 0e7263dc0..c0111a8a1 100644 --- a/server/src/main/java/com/bfwg/service/impl/UserServiceImpl.java +++ b/backend/src/main/java/com/bfwg/service/impl/UserServiceImpl.java @@ -16,6 +16,8 @@ import java.util.List; +import javax.transaction.Transactional; + /** * Created by fan.jin on 2016-10-15. */ @@ -58,6 +60,7 @@ public List findAll() throws AccessDeniedException { } @Override + @Transactional public User save(UserRequest userRequest) { User user = new User(); user.setUsername(userRequest.getUsername()); diff --git a/server/src/main/resources/application.yml b/backend/src/main/resources/application.yml similarity index 84% rename from server/src/main/resources/application.yml rename to backend/src/main/resources/application.yml index 96db5a6f9..b4c1e5ca2 100644 --- a/server/src/main/resources/application.yml +++ b/backend/src/main/resources/application.yml @@ -7,6 +7,10 @@ jwt: secret: queenvictoria cookie: AUTH-TOKEN +spring: + jpa: + open-in-view: false + logging: level: org.springframework.web: ERROR diff --git a/server/src/main/resources/banner.txt b/backend/src/main/resources/banner.txt similarity index 100% rename from server/src/main/resources/banner.txt rename to backend/src/main/resources/banner.txt diff --git a/server/src/main/resources/import.sql b/backend/src/main/resources/import.sql similarity index 100% rename from server/src/main/resources/import.sql rename to backend/src/main/resources/import.sql diff --git a/server/src/test/java/com/bfwg/AbstractTest.java b/backend/src/test/java/com/bfwg/AbstractTest.java similarity index 99% rename from server/src/test/java/com/bfwg/AbstractTest.java rename to backend/src/test/java/com/bfwg/AbstractTest.java index a1b9c0f47..79008834c 100644 --- a/server/src/test/java/com/bfwg/AbstractTest.java +++ b/backend/src/test/java/com/bfwg/AbstractTest.java @@ -73,7 +73,6 @@ protected User buildTestUser() { return user; } - protected User buildTestAdmin() { Authority userAuthority = new Authority(); Authority adminAuthority = new Authority(); diff --git a/server/src/test/java/com/bfwg/MockMvcConfig.java b/backend/src/test/java/com/bfwg/MockMvcConfig.java similarity index 100% rename from server/src/test/java/com/bfwg/MockMvcConfig.java rename to backend/src/test/java/com/bfwg/MockMvcConfig.java diff --git a/server/src/test/java/com/bfwg/security/TokenHelperTest.java b/backend/src/test/java/com/bfwg/security/TokenHelperTest.java similarity index 100% rename from server/src/test/java/com/bfwg/security/TokenHelperTest.java rename to backend/src/test/java/com/bfwg/security/TokenHelperTest.java diff --git a/server/src/test/java/com/bfwg/service/UserServiceTest.java b/backend/src/test/java/com/bfwg/service/UserServiceTest.java similarity index 100% rename from server/src/test/java/com/bfwg/service/UserServiceTest.java rename to backend/src/test/java/com/bfwg/service/UserServiceTest.java diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..8000569bc --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,31 @@ +# Define the services/containers to be run +services: + frontend: # name of the first service + build: frontend # specify the directory of the Dockerfile + # volumes: + # - .:/home/app + # - /home/app/node_modules + ports: + - "80:4200" + networks: + - angular-spring-network + depends_on: + - backend + links: + - backend + # - springboot:springboot + # hostname: frontend + # container_name: frontend + + backend: #name of the second service + build: backend # specify the directory of the Dockerfile + ports: + - "8080" #specify ports forwarding + # expose: + # - "8080" + networks: + - angular-spring-network + # hostname: backend + # https://success.docker.com/Architecture/Docker_Reference_Architecture%3A_Designing_Scalable%2C_Portable_Docker_Container_Networks +networks: + angular-spring-network: \ No newline at end of file diff --git a/frontend/.dockerignore b/frontend/.dockerignore new file mode 100644 index 000000000..cc012bab1 --- /dev/null +++ b/frontend/.dockerignore @@ -0,0 +1,41 @@ +# CVS +.git +.gitignore + +# compiled output +dist +tmp +sme + +# dependencies +node_modules +bower_components + +# IDEs and editors +.idea +.vscode +.project +.classpath +*.launch +.editorconfig +.settings/ +nbproject/ + +# misc +.sass-cache +connect.lock +coverage/* +libpeerconnection.log +npm-debug.log +testem.log +typings +README.md +Dockerfile* + +# e2e +e2e/*.js +e2e/*.map + +#System Files +.DS_Store +Thumbs.db diff --git a/frontend/README.md b/frontend/README.md index d072b1df4..9abdd0435 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -1,7 +1,7 @@ # Angular Spring Boot JWT Starter -This sub-project is the frontend UI portion of the project and it was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.3.21. +This sub-project is the frontend UI portion of the project and it was generated with [Angular CLI](https://github.com/angular/angular-cli) version 9.1.13. -**Make sure you also have NPM 6.9.0, Node 10.15.3 and angular-cli@8.3.21 globally installed** +**Make sure you also have NPM 6.9.0, Node 10.15.3 and angular-cli@9.1.13 globally installed** ## File Structure ``` @@ -49,7 +49,7 @@ Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app w ## Build -Run `ng build` to build the project. The build artifacts will be stored in the `../server/src/main/resorces/static/` directory. Use the `-prod` flag for a production build. +Run `ng build` to build the project. The build artifacts will be stored in the `../server/src/main/resources/static/` directory. Use the `-prod` flag for a production build. ## Running unit tests diff --git a/frontend/dockerfile b/frontend/dockerfile new file mode 100644 index 000000000..b43a45853 --- /dev/null +++ b/frontend/dockerfile @@ -0,0 +1,25 @@ +FROM node:14-alpine + +RUN addgroup -S app && adduser -S -g app app \ +&& apk add --no-cache build-base python3 curl \ +&& apk update && apk upgrade + +# https://www.alpinelinux.org/posts/Docker-image-vulnerability-CVE-2019-5021.html +# https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-5021 +# make sure root login is disabled +RUN sed -i -e 's/^root::/root:!:/' /etc/shadow + +WORKDIR /home/app + +COPY . $WORKDIR + +RUN chown -R app:app /home/app + +USER app + +# RUN npm install +RUN npm i + +EXPOSE 4200 + +CMD ["npm", "run","start-container"] diff --git a/frontend/nginx-custom.conf b/frontend/nginx-custom.conf new file mode 100644 index 000000000..99ab5c67a --- /dev/null +++ b/frontend/nginx-custom.conf @@ -0,0 +1,23 @@ +server { + listen 80; + + gzip on; + gzip_http_version 1.1; + gzip_disable "MSIE [1-6]\."; + gzip_min_length 1100; + gzip_vary on; + gzip_proxied expired no-cache no-store private auth; + gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript; + gzip_comp_level 5; + + root /usr/share/nginx/html; + + location / { + index index.html index.htm; + try_files $uri $uri/ /index.html =404; + } + + location ~ ^/api { + proxy_pass http://angular-spring-starter-backend:8080; + } +} \ No newline at end of file diff --git a/frontend/package.json b/frontend/package.json index 376a2b03d..8794e05f2 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -4,46 +4,49 @@ "scripts": { "ng": "ng", "start": "ng serve --proxy-config proxy.conf.json", + "start-prod": "ng serve --proxy-config proxy.conf.json --prod=true", + "start-container": "ng serve --host=`echo $(hostname -i)` --proxy-config proxy-container.conf.json --prod=true", "build": "ng build", + "build-prod": "ng build --prod=true", "test": "ng test", "lint": "ng lint", "e2e": "ng e2e" }, "private": true, "dependencies": { - "@angular/animations": "^9.1.3", - "@angular/cdk": "^9.2.1", - "@angular/common": "^9.1.3", - "@angular/compiler": "^9.1.3", - "@angular/core": "^9.1.3", + "@angular/animations": "^9.1.13", + "@angular/cdk": "^9.2.4", + "@angular/common": "^9.1.13", + "@angular/compiler": "^9.1.13", + "@angular/core": "^9.1.13", "@angular/flex-layout": "^9.0.0-beta.29", - "@angular/forms": "^9.1.3", - "@angular/material": "^9.2.1", - "@angular/platform-browser": "^9.1.3", - "@angular/platform-browser-dynamic": "^9.1.3", - "@angular/router": "^9.1.3", - "rxjs": "~6.5.4", - "tslib": "^1.11.1", + "@angular/forms": "^9.1.13", + "@angular/material": "^9.2.4", + "@angular/platform-browser": "^9.1.13", + "@angular/platform-browser-dynamic": "^9.1.13", + "@angular/router": "^9.1.13", + "rxjs": "~6.6.7", + "tslib": "^1.14.1", "zone.js": "^0.10.3" }, "devDependencies": { "@angular-devkit/build-angular": "^0.901.3", - "@angular/cli": "^9.1.3", - "@angular/compiler-cli": "^9.1.3", - "@angular/language-service": "^9.1.3", - "@types/jasmine": "^3.5.10", - "@types/jasminewd2": "~2.0.3", - "@types/node": "^12.12.37", + "@angular/cli": "^9.1.15", + "@angular/compiler-cli": "^9.1.13", + "@angular/language-service": "^9.1.13", + "@types/jasmine": "^3.10.18", + "@types/jasminewd2": "~2.0.13", + "@types/node": "^12.20.55", "codelyzer": "^5.2.2", - "jasmine-core": "~3.5.0", + "jasmine-core": "~3.99.1", "jasmine-spec-reporter": "~4.2.1", - "karma": "^5.0.2", - "karma-chrome-launcher": "~3.1.0", - "karma-coverage-istanbul-reporter": "~2.1.0", - "karma-jasmine": "~3.0.1", - "karma-jasmine-html-reporter": "^1.5.3", + "karma": "^5.2.3", + "karma-chrome-launcher": "~3.1.1", + "karma-coverage-istanbul-reporter": "~2.1.1", + "karma-jasmine": "~3.3.1", + "karma-jasmine-html-reporter": "^1.7.0", "protractor": "^5.4.4", - "ts-node": "~8.3.0", + "ts-node": "~8.10.2", "tslint": "~6.1.0", "typescript": "~3.8.3" } diff --git a/frontend/proxy-container.conf.json b/frontend/proxy-container.conf.json new file mode 100644 index 000000000..cdb54c897 --- /dev/null +++ b/frontend/proxy-container.conf.json @@ -0,0 +1,6 @@ +{ + "/api": { + "target": "http://backend:8080", + "secure": false + } +} diff --git a/frontend/src/app/component/api-card/api-card.component.ts b/frontend/src/app/component/api-card/api-card.component.ts index 9a99cbf22..0b239da7e 100644 --- a/frontend/src/app/component/api-card/api-card.component.ts +++ b/frontend/src/app/component/api-card/api-card.component.ts @@ -21,11 +21,15 @@ export class ApiCardComponent implements OnInit { } ngOnInit() { - console.log(this.responseObj); + // console.log(this.responseObj); } onButtonClick() { - this.expand = true; + if (this.expand) { + this.expand = false; + } else { + this.expand = true; + } this.apiClick.next(this.apiText); } diff --git a/frontend/src/app/component/header/account-menu/account-menu.component.ts b/frontend/src/app/component/header/account-menu/account-menu.component.ts index 24cd87d51..043144f62 100644 --- a/frontend/src/app/component/header/account-menu/account-menu.component.ts +++ b/frontend/src/app/component/header/account-menu/account-menu.component.ts @@ -9,7 +9,7 @@ import {Router} from '@angular/router'; }) export class AccountMenuComponent implements OnInit { - // TODO define user interface + // TODO: define user interface user: any; constructor(