├── README.md ├── backend └── imperblog-backend │ ├── .gitignore │ ├── .mvn │ └── wrapper │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ ├── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── aureocarmelino │ │ │ │ └── imperblogbackend │ │ │ │ ├── ImperblogBackendApplication.java │ │ │ │ ├── api │ │ │ │ ├── controller │ │ │ │ │ ├── AuthenticationController.java │ │ │ │ │ ├── ChartController.java │ │ │ │ │ ├── PostController.java │ │ │ │ │ └── TokenController.java │ │ │ │ ├── exception │ │ │ │ │ ├── Field.java │ │ │ │ │ ├── GlobalExceptionHandler.java │ │ │ │ │ └── Problem.java │ │ │ │ ├── repository │ │ │ │ │ ├── AuthorRepository.java │ │ │ │ │ └── PostRepository.java │ │ │ │ └── service │ │ │ │ │ ├── AuthenticationService.java │ │ │ │ │ ├── ChartService.java │ │ │ │ │ └── PostService.java │ │ │ │ ├── config │ │ │ │ ├── CorsFilterConfig.java │ │ │ │ ├── ModelMapperConfig.java │ │ │ │ ├── PasswordEncoderConfig.java │ │ │ │ └── SecretKey.java │ │ │ │ ├── model │ │ │ │ ├── dto │ │ │ │ │ ├── SignUpDto │ │ │ │ │ │ └── SignUpDto.java │ │ │ │ │ ├── chartDto │ │ │ │ │ │ └── ChartDto.java │ │ │ │ │ ├── loginDto │ │ │ │ │ │ └── LoginDto.java │ │ │ │ │ └── postDto │ │ │ │ │ │ └── CreatePostDto.java │ │ │ │ ├── entity │ │ │ │ │ ├── Author.java │ │ │ │ │ ├── Gender.java │ │ │ │ │ └── Post.java │ │ │ │ └── exception │ │ │ │ │ ├── BusinessException.java │ │ │ │ │ └── ObjectNotFoundException.java │ │ │ │ ├── security │ │ │ │ ├── AuthorDetailsServiceImpl.java │ │ │ │ ├── AuthorSystem.java │ │ │ │ ├── oauth2 │ │ │ │ │ ├── AuthorizationServerConfig.java │ │ │ │ │ └── ResourceServerConfig.java │ │ │ │ └── token │ │ │ │ │ ├── CustomTokenEncher.java │ │ │ │ │ ├── RefreshTokenCookiePreProcessorFilter.java │ │ │ │ │ └── RefreshTokenPostProcessor.java │ │ │ │ └── util │ │ │ │ └── constant │ │ │ │ ├── ConstantAuthentication.java │ │ │ │ ├── ConstantGlobalExceptionHandler.java │ │ │ │ └── ConstantPost.java │ │ └── resources │ │ │ ├── application.properties │ │ │ └── db │ │ │ └── migration │ │ │ ├── V01__create_table_author.sql │ │ │ └── V02__create_table_post.sql │ └── test │ │ └── java │ │ └── com │ │ └── aureocarmelino │ │ └── imperblogbackend │ │ ├── ImperblogBackendApplicationTests.java │ │ └── api │ │ ├── controller │ │ ├── AuthenticationControllerTest.java │ │ ├── ChartControllerTest.java │ │ ├── PostControllerTest.java │ │ └── TokenControllerTest.java │ │ ├── repository │ │ ├── AuthorRepositoryTest.java │ │ └── PostRepositoryTest.java │ │ └── service │ │ ├── AuthenticationServiceTest.java │ │ ├── ChartServiceTest.java │ │ └── PostServiceTest.java │ └── system.properties ├── frontend-angular └── imperblog-frontend │ ├── .browserslistrc │ ├── .editorconfig │ ├── .gitignore │ ├── README.md │ ├── angular.json │ ├── karma.conf.js │ ├── package-lock.json │ ├── package.json │ ├── src │ ├── app │ │ ├── app-routing.module.ts │ │ ├── app.component.css │ │ ├── app.component.html │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ ├── components │ │ │ ├── header-profile │ │ │ │ ├── header-profile.component.css │ │ │ │ ├── header-profile.component.html │ │ │ │ └── header-profile.component.ts │ │ │ ├── header │ │ │ │ ├── header.component.css │ │ │ │ ├── header.component.html │ │ │ │ └── header.component.ts │ │ │ ├── home-page │ │ │ │ ├── home-page.component.css │ │ │ │ ├── home-page.component.html │ │ │ │ └── home-page.component.ts │ │ │ ├── login │ │ │ │ ├── login.component.css │ │ │ │ ├── login.component.html │ │ │ │ └── login.component.ts │ │ │ ├── page-not-found │ │ │ │ ├── page-not-found.component.css │ │ │ │ ├── page-not-found.component.html │ │ │ │ └── page-not-found.component.ts │ │ │ ├── profile │ │ │ │ ├── profile.component.css │ │ │ │ ├── profile.component.html │ │ │ │ └── profile.component.ts │ │ │ └── register │ │ │ │ ├── register.component.css │ │ │ │ ├── register.component.html │ │ │ │ └── register.component.ts │ │ ├── guard-routes │ │ │ └── imperblogauth.guard.ts │ │ ├── interceptor │ │ │ └── imper-blog-http-interceptor.ts │ │ ├── models │ │ │ ├── Author.ts │ │ │ ├── ChartDto.ts │ │ │ ├── CreatePostDto.ts │ │ │ ├── LoginDto.ts │ │ │ └── Post.ts │ │ └── services │ │ │ ├── add-post.service.ts │ │ │ ├── auth.service.ts │ │ │ ├── error-handler.service.ts │ │ │ ├── list-post.service.ts │ │ │ ├── logout.service.ts │ │ │ └── signup.service.ts │ ├── assets │ │ ├── .gitkeep │ │ └── img │ │ │ ├── crown.png │ │ │ ├── emptyList.png │ │ │ ├── error-404.png │ │ │ └── login.avif │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── favicon.ico │ ├── index.html │ ├── main.ts │ ├── polyfills.ts │ ├── styles.css │ └── test.ts │ ├── tsconfig.app.json │ ├── tsconfig.json │ └── tsconfig.spec.json ├── myassets ├── blog_app.gif ├── create_database.PNG ├── login.PNG ├── print_postman_collection.PNG ├── run_test.PNG ├── run_test2.PNG ├── unit_run_tests.gif ├── unit_test.png └── unit_test2.png └── postman_collection └── ImperBlog.postman_collection.json /README.md: -------------------------------------------------------------------------------- 1 | # FullStack App ( Spring + Angular ) : ImperBlog :bookmark_tabs: 2 | 3 | **1) ENGLISH** 4 | 5 | Application for creating blog posts, developed with Java using Spring Framework for backend and Angular along with PrimeNG Library for frontend development. 6 | 7 | **1) PORTUGUESE** 8 | 9 | Aplicação para a criação de postagens ao estilo BLOG, desenvolvida com Java utilizando o Spring Framework para o backend e o Angular juntamente com a Biblioteca PrimeNG para o desenvolvimento frontend. 10 | 11 | **2) ENGLISH** 12 | 13 | ###### `The App has the following features:` 14 | 15 | 1. Creating a user account 16 | 2. Login access validation using Spring Security 17 | 3. Creating multiple posts per user 18 | 4. Listagem de todas as postagens na página inicial 19 | 5. Listing of all posts on the homepage 20 | 6. view a post 21 | 7. Edit a logged-in user's post 22 | 8. Delete a post from the logged in user 23 | 9. View the graph of users by gender (M & F) 24 | 9. Responsive design suitable for most devices 25 | 10. Logout 26 | 11. JWT Token Usage 27 | 12. Update token automatically 28 | 13. Frontend URL protection 29 | 14. Redirection to login when token expires 30 | 14. Backend unit tests in Controllers, Services and Repositories classes 31 | 15. And additional features or functionality in both Backend and Frontend for better user usability 32 | 33 | **2) PORTUGUESE** 34 | 35 | **`O App conta com as seguintes funcionalidades:`** 36 | 37 | 1. Criação de uma conta de utilizador 38 | 2. Validação de acesso com login utilizando Spring Security 39 | 3. Criação de várias postagens por utilizador 40 | 4. Listagem de todas as postagens na página inicial 41 | 5. Listagem de todas as postagens de um utilizador no seu perfil 42 | 6. Visualizar uma postagem 43 | 7. Editar uma postagem do utilizador logado 44 | 8. Deletar uma postagem do utilizador logado 45 | 9. Visualizar o gráfico de utilizadores por gênero ( M & F ) 46 | 9. Design Responsivo adequado para a maioria dos dispositivos 47 | 10. Logout 48 | 11. Utilização do Token JWT 49 | 12. Refresh Token automático 50 | 13. Proteção de URL's no frontend 51 | 14. Redirecionamento para o login quando o token expira 52 | 15. Testes unitários no Backend nas classes dos Controllers, Services e Repositories 53 | 16. E recursos ou funcionalidades adicionais tanto no Backend como no Frontend para melhor usabilidade do usuário 54 | 55 | **EN: The final system is available for testing at:** **[https://imperblog.vercel.app/](https://imperblog.vercel.app/)** 56 | 57 | **PT: O sistema final encontra-se disponível para teste em :** **[https://imperblog.vercel.app/](https://imperblog.vercel.app/)** 58 | 59 |

60 | 61 |

62 | 63 | 64 | # :key: **EN:** Requirements to run on Localhost 65 | 66 | 67 | 68 | # :key: **PT:** Requisitos para executar em Localhost 69 | 70 | 1. **Java Jdk** ( **8/11** ) 71 | 2. **Spring Tools Suit** | **Eclipse IDE** | **IntelliJ IDEA** | **NetBeans** : Backend 72 | 3. **VScode**: Frontend 73 | 4. **PostMan** 74 | 5. **Node Js & npm** 75 | 6. **TypeScript** 76 | 7. **Angular CLI** 77 | 8. **PgAdmin 4 & PostgreSQL** 78 | 9. **Maven** 79 | 80 | 81 | 82 | # :interrobang: **EN:** Note ( Database creation ) 83 | 84 | 1. It is important that you first create the database with the name **imperblog_bd** before running the backend if you have **PgAdmin 4** & **PostgreSQL** installed on your machine as shown in the following image: 85 | 86 | 87 | 88 | # :interrobang: **PT:** Observação ( Criação do banco de dados ) 89 | 90 | 1. É importante que primeiramente crie o banco de dados com o nome **imperblog_bd** antes de executar o backend caso tenha o **PgAdmin 4** & **PostgreSQL** instalados na sua máquina como mostra a imagem a seguir: 91 | 92 |

93 | 94 |

95 | 96 | 97 | **EN** 98 | 99 | 1. When starting the backend application, the database will automatically be loaded with the respective tables defined in `imperblog-backend/src/main/db/migration` 100 | 2. If you want to change the name of the database, create one of your choice and remember to change it in the properties file `application.properties` located in the directory `imperblog-backend/src/main/resources ` on line 1 `spring.datasource.url=jdbc:postgresql://127.0.0.1:5432/your-database-name` 101 | 102 | **PT** 103 | 104 | 1. Ao iniciar a aplicação backend , automaticamente o banco de dados será carregado com as respetivas tabelas definidas em `imperblog-backend/src/main/db/migration` 105 | 2. Caso deseje alterar a nome do banco de dados, crie um a sua escolha e lembre-se de alterá-lo no ficheiro de propriedades `application.properties` que encontra-se no diretório `imperblog-backend/src/main/resources` na linha nº 1 `spring.datasource.url=jdbc:postgresql://127.0.0.1:5432/nome-do-seu-banco-de-dados` 106 | 107 | 108 | 109 | # :computer: **EN:** Installation :computer_mouse: 110 | 111 | **To perform the installation, you must have a good internet connection and follow the steps below** :arrow_down_small: 112 | 113 | 1. **Downloading files from GitHub** : In **Code** use the **Download ZIP** option to **download** the entire repository. 114 | 2. **Unzip the downloaded file**. 115 | 3. **Open the `imperblog-backend` directory** in your preferred backend IDE and wait for Maven to import/download the dependencies. 116 | 4. **Run the project** `imperblog-backend` **so that it is available at `localhost:8080`** . 117 | 5. **Open the directory** `imperblog-frontend` in your preferred frontend IDE, and at the root of the directory, open the command line and run the command `npm install` to install the Angular framework dependencies and PrimeNG. 118 | 6. Run, typing in **GitBash** the command `ng serve -o` in the root of the `imperblog-frontend` project, so that the application starts and the browser automatically opens or type `ng serve` and access the url o address: `http://localhost:4200`. 119 | 7. If everything is perfectly installed, accessing the url `http://localhost:4200` will have access to the Login as shown in the following image: 120 | 121 | # **PT:** Instalação :computer_mouse: 122 | 123 | **Para realizar a instalação, é necessário possuir uma boa conexão com a internet e seguir os passos a baixo** :arrow_down_small: 124 | 125 | 1. **Baixando os arquivos do GitHub** : Em **Code** use a opção **Download ZIP** para **baixar** todo o repositório. 126 | 2. **Descompactar o arquivo baixado**. 127 | 3. **Abrir o diretório** `imperblog-backend` em sua IDE backend preferida e esperar que o Maven faça o import/download das dependências. 128 | 4. **Executar o projecto** `imperblog-backend` **para que o mesmo fique disponível em `localhost:8080`** . 129 | 5. **Abrir o diretório** `imperblog-frontend` em sua IDE frontend preferida , e na raiz do diretório, abra a linha de comando e execute o comando`npm install` para que se faça a instalação das dependências do framework Angular e PrimeNG . 130 | 6. Execute, digitando no **GitBash** o comando `ng serve -o` na raiz do projeto `imperblog-frontend` , para que a aplicação inicie e o browser abra automaticamente ou digite `ng serve` e aceda na url o endereço: `http://localhost:4200`. 131 | 7. Caso tudo esteja perfeitamente instalado ao aceder a url `http://localhost:4200` terá acesso ao Login como mostra a imagem a seguir: 132 | 133 |

134 | 135 |

136 | 137 | # :computer: **EN:** Unitary tests :bar_chart: 138 | 139 | **Unit tests are in the directory:** `imperblog-backend/src/test/java` 140 | 141 | To run all tests in **simultaneous** do as shown in the following image:arrow_down_small: 142 | 143 | # :computer: **PT:** Testes Unitários :bar_chart: 144 | 145 | **Os testes unitários encontram-se no diretório:** `imperblog-backend/src/test/java` 146 | 147 | Para executar todos os testes em **simultâneo** faça como mostra a imagem a seguir :arrow_down_small: 148 | 149 |

150 | 151 |

152 | 153 | 154 | **EN:** And as a result we will have all the tests executed :arrow_down_small: 155 | 156 | **PT:** E como resultado teremos todos os testes executados :arrow_down_small: 157 | 158 |

159 | 160 |

161 | 162 |

163 | 164 |

165 | 166 | 167 | **EN:** To run the tests of a specific class, in which we will use the **PostServiceTest** class as an example, do as shown in the following image :arrow_down_small: 168 | 169 | 170 | 171 | **PT:** Para executar os testes de uma classe específica, em que utilizaremos a classe **PostServiceTest** como exemplo faça como mostra a imagem a seguir :arrow_down_small: 172 | 173 |

174 | 175 |

176 | 177 | 178 | 179 | **EN:** And as a result we will have only the tests of the **PostServiceTest** class executed :arrow_down_small: 180 | 181 | **PT:** E como resultado teremos somentes os testes da classe **PostServiceTest** executados :arrow_down_small: 182 | 183 |

184 | 185 |

186 | 187 | 188 | # :computer: Postman Collection :satellite: 189 | 190 | 191 | 192 | `/postman_collection` 193 | 194 | 195 | 196 |

197 | 198 |

199 | 200 | 201 | **EN: I'll be adding new features as time goes on, I hope you like it** :heart: 202 | 203 | **PT: Vou adicionando novas funcionalidades consoante o tempo, espero que tenham gostado** :heart: 204 | 205 | # SEJA LUZ :bulb: BE LIGHT:bulb: 206 | 207 | 208 | 209 | -------------------------------------------------------------------------------- /backend/imperblog-backend/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /backend/imperblog-backend/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aureocarmelino/fullstack-blog-angular-and-spring/994e46b1dc948d7265f505d679b44c9264c5139e/backend/imperblog-backend/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /backend/imperblog-backend/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar 3 | -------------------------------------------------------------------------------- /backend/imperblog-backend/mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # https://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /usr/local/etc/mavenrc ] ; then 40 | . /usr/local/etc/mavenrc 41 | fi 42 | 43 | if [ -f /etc/mavenrc ] ; then 44 | . /etc/mavenrc 45 | fi 46 | 47 | if [ -f "$HOME/.mavenrc" ] ; then 48 | . "$HOME/.mavenrc" 49 | fi 50 | 51 | fi 52 | 53 | # OS specific support. $var _must_ be set to either true or false. 54 | cygwin=false; 55 | darwin=false; 56 | mingw=false 57 | case "`uname`" in 58 | CYGWIN*) cygwin=true ;; 59 | MINGW*) mingw=true;; 60 | Darwin*) darwin=true 61 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 62 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 63 | if [ -z "$JAVA_HOME" ]; then 64 | if [ -x "/usr/libexec/java_home" ]; then 65 | export JAVA_HOME="`/usr/libexec/java_home`" 66 | else 67 | export JAVA_HOME="/Library/Java/Home" 68 | fi 69 | fi 70 | ;; 71 | esac 72 | 73 | if [ -z "$JAVA_HOME" ] ; then 74 | if [ -r /etc/gentoo-release ] ; then 75 | JAVA_HOME=`java-config --jre-home` 76 | fi 77 | fi 78 | 79 | if [ -z "$M2_HOME" ] ; then 80 | ## resolve links - $0 may be a link to maven's home 81 | PRG="$0" 82 | 83 | # need this for relative symlinks 84 | while [ -h "$PRG" ] ; do 85 | ls=`ls -ld "$PRG"` 86 | link=`expr "$ls" : '.*-> \(.*\)$'` 87 | if expr "$link" : '/.*' > /dev/null; then 88 | PRG="$link" 89 | else 90 | PRG="`dirname "$PRG"`/$link" 91 | fi 92 | done 93 | 94 | saveddir=`pwd` 95 | 96 | M2_HOME=`dirname "$PRG"`/.. 97 | 98 | # make it fully qualified 99 | M2_HOME=`cd "$M2_HOME" && pwd` 100 | 101 | cd "$saveddir" 102 | # echo Using m2 at $M2_HOME 103 | fi 104 | 105 | # For Cygwin, ensure paths are in UNIX format before anything is touched 106 | if $cygwin ; then 107 | [ -n "$M2_HOME" ] && 108 | M2_HOME=`cygpath --unix "$M2_HOME"` 109 | [ -n "$JAVA_HOME" ] && 110 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 111 | [ -n "$CLASSPATH" ] && 112 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 113 | fi 114 | 115 | # For Mingw, ensure paths are in UNIX format before anything is touched 116 | if $mingw ; then 117 | [ -n "$M2_HOME" ] && 118 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 119 | [ -n "$JAVA_HOME" ] && 120 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 121 | fi 122 | 123 | if [ -z "$JAVA_HOME" ]; then 124 | javaExecutable="`which javac`" 125 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 126 | # readlink(1) is not available as standard on Solaris 10. 127 | readLink=`which readlink` 128 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 129 | if $darwin ; then 130 | javaHome="`dirname \"$javaExecutable\"`" 131 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 132 | else 133 | javaExecutable="`readlink -f \"$javaExecutable\"`" 134 | fi 135 | javaHome="`dirname \"$javaExecutable\"`" 136 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 137 | JAVA_HOME="$javaHome" 138 | export JAVA_HOME 139 | fi 140 | fi 141 | fi 142 | 143 | if [ -z "$JAVACMD" ] ; then 144 | if [ -n "$JAVA_HOME" ] ; then 145 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 146 | # IBM's JDK on AIX uses strange locations for the executables 147 | JAVACMD="$JAVA_HOME/jre/sh/java" 148 | else 149 | JAVACMD="$JAVA_HOME/bin/java" 150 | fi 151 | else 152 | JAVACMD="`\\unset -f command; \\command -v java`" 153 | fi 154 | fi 155 | 156 | if [ ! -x "$JAVACMD" ] ; then 157 | echo "Error: JAVA_HOME is not defined correctly." >&2 158 | echo " We cannot execute $JAVACMD" >&2 159 | exit 1 160 | fi 161 | 162 | if [ -z "$JAVA_HOME" ] ; then 163 | echo "Warning: JAVA_HOME environment variable is not set." 164 | fi 165 | 166 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 167 | 168 | # traverses directory structure from process work directory to filesystem root 169 | # first directory with .mvn subdirectory is considered project base directory 170 | find_maven_basedir() { 171 | 172 | if [ -z "$1" ] 173 | then 174 | echo "Path not specified to find_maven_basedir" 175 | return 1 176 | fi 177 | 178 | basedir="$1" 179 | wdir="$1" 180 | while [ "$wdir" != '/' ] ; do 181 | if [ -d "$wdir"/.mvn ] ; then 182 | basedir=$wdir 183 | break 184 | fi 185 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 186 | if [ -d "${wdir}" ]; then 187 | wdir=`cd "$wdir/.."; pwd` 188 | fi 189 | # end of workaround 190 | done 191 | echo "${basedir}" 192 | } 193 | 194 | # concatenates all lines of a file 195 | concat_lines() { 196 | if [ -f "$1" ]; then 197 | echo "$(tr -s '\n' ' ' < "$1")" 198 | fi 199 | } 200 | 201 | BASE_DIR=`find_maven_basedir "$(pwd)"` 202 | if [ -z "$BASE_DIR" ]; then 203 | exit 1; 204 | fi 205 | 206 | ########################################################################################## 207 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 208 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 209 | ########################################################################################## 210 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then 211 | if [ "$MVNW_VERBOSE" = true ]; then 212 | echo "Found .mvn/wrapper/maven-wrapper.jar" 213 | fi 214 | else 215 | if [ "$MVNW_VERBOSE" = true ]; then 216 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." 217 | fi 218 | if [ -n "$MVNW_REPOURL" ]; then 219 | jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" 220 | else 221 | jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" 222 | fi 223 | while IFS="=" read key value; do 224 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;; 225 | esac 226 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" 227 | if [ "$MVNW_VERBOSE" = true ]; then 228 | echo "Downloading from: $jarUrl" 229 | fi 230 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" 231 | if $cygwin; then 232 | wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` 233 | fi 234 | 235 | if command -v wget > /dev/null; then 236 | if [ "$MVNW_VERBOSE" = true ]; then 237 | echo "Found wget ... using wget" 238 | fi 239 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 240 | wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" 241 | else 242 | wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" 243 | fi 244 | elif command -v curl > /dev/null; then 245 | if [ "$MVNW_VERBOSE" = true ]; then 246 | echo "Found curl ... using curl" 247 | fi 248 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 249 | curl -o "$wrapperJarPath" "$jarUrl" -f 250 | else 251 | curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f 252 | fi 253 | 254 | else 255 | if [ "$MVNW_VERBOSE" = true ]; then 256 | echo "Falling back to using Java to download" 257 | fi 258 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" 259 | # For Cygwin, switch paths to Windows format before running javac 260 | if $cygwin; then 261 | javaClass=`cygpath --path --windows "$javaClass"` 262 | fi 263 | if [ -e "$javaClass" ]; then 264 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 265 | if [ "$MVNW_VERBOSE" = true ]; then 266 | echo " - Compiling MavenWrapperDownloader.java ..." 267 | fi 268 | # Compiling the Java class 269 | ("$JAVA_HOME/bin/javac" "$javaClass") 270 | fi 271 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 272 | # Running the downloader 273 | if [ "$MVNW_VERBOSE" = true ]; then 274 | echo " - Running MavenWrapperDownloader.java ..." 275 | fi 276 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") 277 | fi 278 | fi 279 | fi 280 | fi 281 | ########################################################################################## 282 | # End of extension 283 | ########################################################################################## 284 | 285 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 286 | if [ "$MVNW_VERBOSE" = true ]; then 287 | echo $MAVEN_PROJECTBASEDIR 288 | fi 289 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 290 | 291 | # For Cygwin, switch paths to Windows format before running java 292 | if $cygwin; then 293 | [ -n "$M2_HOME" ] && 294 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 295 | [ -n "$JAVA_HOME" ] && 296 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 297 | [ -n "$CLASSPATH" ] && 298 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 299 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 300 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 301 | fi 302 | 303 | # Provide a "standardized" way to retrieve the CLI args that will 304 | # work with both Windows and non-Windows executions. 305 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" 306 | export MAVEN_CMD_LINE_ARGS 307 | 308 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 309 | 310 | exec "$JAVACMD" \ 311 | $MAVEN_OPTS \ 312 | $MAVEN_DEBUG_OPTS \ 313 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 314 | "-Dmaven.home=${M2_HOME}" \ 315 | "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 316 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 317 | -------------------------------------------------------------------------------- /backend/imperblog-backend/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* 50 | if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" 124 | 125 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 127 | ) 128 | 129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 131 | if exist %WRAPPER_JAR% ( 132 | if "%MVNW_VERBOSE%" == "true" ( 133 | echo Found %WRAPPER_JAR% 134 | ) 135 | ) else ( 136 | if not "%MVNW_REPOURL%" == "" ( 137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" 138 | ) 139 | if "%MVNW_VERBOSE%" == "true" ( 140 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 141 | echo Downloading from: %DOWNLOAD_URL% 142 | ) 143 | 144 | powershell -Command "&{"^ 145 | "$webclient = new-object System.Net.WebClient;"^ 146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 148 | "}"^ 149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ 150 | "}" 151 | if "%MVNW_VERBOSE%" == "true" ( 152 | echo Finished downloading %WRAPPER_JAR% 153 | ) 154 | ) 155 | @REM End of extension 156 | 157 | @REM Provide a "standardized" way to retrieve the CLI args that will 158 | @REM work with both Windows and non-Windows executions. 159 | set MAVEN_CMD_LINE_ARGS=%* 160 | 161 | %MAVEN_JAVA_EXE% ^ 162 | %JVM_CONFIG_MAVEN_PROPS% ^ 163 | %MAVEN_OPTS% ^ 164 | %MAVEN_DEBUG_OPTS% ^ 165 | -classpath %WRAPPER_JAR% ^ 166 | "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ 167 | %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 168 | if ERRORLEVEL 1 goto error 169 | goto end 170 | 171 | :error 172 | set ERROR_CODE=1 173 | 174 | :end 175 | @endlocal & set ERROR_CODE=%ERROR_CODE% 176 | 177 | if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost 178 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 179 | if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" 180 | if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" 181 | :skipRcPost 182 | 183 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 184 | if "%MAVEN_BATCH_PAUSE%"=="on" pause 185 | 186 | if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% 187 | 188 | cmd /C exit /B %ERROR_CODE% 189 | -------------------------------------------------------------------------------- /backend/imperblog-backend/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.6.3 9 | 10 | 11 | com.aureocarmelino 12 | imperblog-backend 13 | 0.0.1-SNAPSHOT 14 | imperblog-backend 15 | Demo project for Spring Boot 16 | 17 | 11 18 | 19 | 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-test 31 | test 32 | 33 | 34 | 35 | 36 | org.springframework.boot 37 | spring-boot-starter-web 38 | 39 | 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-starter-validation 44 | 45 | 46 | 47 | 48 | org.springframework.boot 49 | spring-boot-devtools 50 | runtime 51 | true 52 | 53 | 54 | 55 | 56 | org.projectlombok 57 | lombok 58 | true 59 | 60 | 61 | 62 | 63 | org.springframework.boot 64 | spring-boot-starter-data-jpa 65 | 66 | 67 | 68 | 69 | org.postgresql 70 | postgresql 71 | runtime 72 | 73 | 74 | 75 | 76 | org.flywaydb 77 | flyway-core 78 | 79 | 80 | 81 | 82 | org.modelmapper 83 | modelmapper 84 | 2.3.7 85 | 86 | 87 | 88 | org.springframework.boot 89 | spring-boot-starter-security 90 | 91 | 92 | 93 | 94 | junit 95 | junit 96 | test 97 | 98 | 99 | 100 | 101 | 102 | org.springframework.boot 103 | spring-boot-starter-security 104 | 105 | 106 | 107 | org.springframework.security.oauth 108 | spring-security-oauth2 109 | 2.3.7.RELEASE 110 | 111 | 112 | 113 | org.springframework.security 114 | spring-security-oauth2-authorization-server 115 | 0.3.0 116 | 117 | 118 | 119 | org.springframework.boot 120 | spring-boot-starter-oauth2-resource-server 121 | 122 | 123 | 124 | com.nimbusds 125 | oauth2-oidc-sdk 126 | 9.24 127 | runtime 128 | 129 | 130 | 131 | 132 | org.springframework.security 133 | spring-security-jwt 134 | 1.1.1.RELEASE 135 | 136 | 137 | 138 | 139 | 140 | com.fasterxml.jackson.core 141 | jackson-databind 142 | 143 | 144 | 145 | 146 | com.h2database 147 | h2 148 | test 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | org.springframework.boot 157 | spring-boot-maven-plugin 158 | 159 | 160 | 161 | org.projectlombok 162 | lombok 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /backend/imperblog-backend/src/main/java/com/aureocarmelino/imperblogbackend/ImperblogBackendApplication.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class ImperblogBackendApplication 8 | { 9 | public static void main(String[] args) 10 | { 11 | SpringApplication.run(ImperblogBackendApplication.class, args); 12 | } 13 | } -------------------------------------------------------------------------------- /backend/imperblog-backend/src/main/java/com/aureocarmelino/imperblogbackend/api/controller/AuthenticationController.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.api.controller; 2 | 3 | import java.net.URI; 4 | import org.springframework.http.ResponseEntity; 5 | import org.springframework.validation.annotation.Validated; 6 | import org.springframework.web.bind.annotation.PostMapping; 7 | import org.springframework.web.bind.annotation.RequestBody; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | import org.springframework.web.servlet.support.ServletUriComponentsBuilder; 11 | import com.aureocarmelino.imperblogbackend.api.service.AuthenticationService; 12 | import com.aureocarmelino.imperblogbackend.model.dto.SignUpDto.SignUpDto; 13 | import com.aureocarmelino.imperblogbackend.model.entity.Author; 14 | import com.aureocarmelino.imperblogbackend.model.exception.BusinessException; 15 | import lombok.AllArgsConstructor; 16 | 17 | 18 | @AllArgsConstructor 19 | @RestController 20 | @RequestMapping("/api/auth") 21 | public class AuthenticationController 22 | { 23 | private AuthenticationService authenticationService; 24 | 25 | private static final String ID = "/{id}"; 26 | 27 | @PostMapping("/signup") 28 | public ResponseEntity signup(@Validated @RequestBody SignUpDto signUpDto) throws BusinessException 29 | { 30 | Author author = this.authenticationService.signup(signUpDto); 31 | 32 | URI uri = ServletUriComponentsBuilder.fromCurrentRequest().path(ID).buildAndExpand(author.getPkAuthor()).toUri(); 33 | 34 | return ResponseEntity.created(uri).build(); 35 | } 36 | } -------------------------------------------------------------------------------- /backend/imperblog-backend/src/main/java/com/aureocarmelino/imperblogbackend/api/controller/ChartController.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.api.controller; 2 | 3 | import java.util.List; 4 | import org.springframework.http.HttpStatus; 5 | import org.springframework.http.ResponseEntity; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.bind.annotation.RestController; 9 | import com.aureocarmelino.imperblogbackend.api.service.ChartService; 10 | import com.aureocarmelino.imperblogbackend.model.dto.chartDto.ChartDto; 11 | import lombok.AllArgsConstructor; 12 | 13 | 14 | @AllArgsConstructor 15 | @RestController 16 | @RequestMapping("/api/chart") 17 | public class ChartController 18 | { 19 | private ChartService chartService; 20 | 21 | @GetMapping 22 | public ResponseEntity> findTotalAuthorsByGender() 23 | { 24 | return ResponseEntity.status(HttpStatus.OK).body(this.chartService.findTotalAuthorsByGender()); 25 | } 26 | } -------------------------------------------------------------------------------- /backend/imperblog-backend/src/main/java/com/aureocarmelino/imperblogbackend/api/controller/PostController.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.api.controller; 2 | 3 | import java.net.URI; 4 | import java.util.List; 5 | 6 | import javax.validation.Valid; 7 | 8 | import org.springframework.http.HttpStatus; 9 | import org.springframework.http.ResponseEntity; 10 | import org.springframework.web.bind.annotation.DeleteMapping; 11 | import org.springframework.web.bind.annotation.GetMapping; 12 | import org.springframework.web.bind.annotation.PathVariable; 13 | import org.springframework.web.bind.annotation.PostMapping; 14 | import org.springframework.web.bind.annotation.PutMapping; 15 | import org.springframework.web.bind.annotation.RequestBody; 16 | import org.springframework.web.bind.annotation.RequestMapping; 17 | import org.springframework.web.bind.annotation.RestController; 18 | import org.springframework.web.servlet.support.ServletUriComponentsBuilder; 19 | 20 | import com.aureocarmelino.imperblogbackend.api.service.PostService; 21 | import com.aureocarmelino.imperblogbackend.model.dto.postDto.CreatePostDto; 22 | import com.aureocarmelino.imperblogbackend.model.entity.Post; 23 | 24 | import lombok.AllArgsConstructor; 25 | 26 | 27 | @AllArgsConstructor 28 | @RestController 29 | @RequestMapping("/api/posts") 30 | public class PostController 31 | { 32 | private PostService postService; 33 | 34 | private static final String ID = "/{id}"; 35 | 36 | @PostMapping 37 | public ResponseEntity createPost(@Valid @RequestBody CreatePostDto createPostDto) 38 | { 39 | Post post = this.postService.create(createPostDto); 40 | 41 | URI uri = ServletUriComponentsBuilder.fromCurrentRequest().path(ID).buildAndExpand(post.getPkPost()).toUri(); 42 | 43 | return ResponseEntity.created(uri).build(); 44 | } 45 | 46 | @PutMapping 47 | public ResponseEntity editPost(@Valid @RequestBody CreatePostDto createPostDto) 48 | { 49 | Post editPost = this.postService.edit(createPostDto); 50 | 51 | return ResponseEntity.status(HttpStatus.OK).body(editPost); 52 | } 53 | 54 | @GetMapping 55 | public ResponseEntity> findAll() 56 | { 57 | return ResponseEntity.status(HttpStatus.OK).body(this.postService.findAll()); 58 | } 59 | 60 | @GetMapping("/user") 61 | public ResponseEntity> findAllAuthorPosts() 62 | { 63 | return ResponseEntity.status(HttpStatus.OK).body(this.postService.findAllAuthorPosts()); 64 | } 65 | 66 | @GetMapping("/{id}") 67 | public ResponseEntity findById(@PathVariable Long id) 68 | { 69 | return ResponseEntity.status(HttpStatus.OK).body(this.postService.findById(id)); 70 | } 71 | 72 | @DeleteMapping("/{id}") 73 | public ResponseEntity deleteById(@PathVariable Long id) 74 | { 75 | this.postService.deleteById(id); 76 | 77 | return ResponseEntity.noContent().build(); 78 | } 79 | } -------------------------------------------------------------------------------- /backend/imperblog-backend/src/main/java/com/aureocarmelino/imperblogbackend/api/controller/TokenController.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.api.controller; 2 | 3 | import javax.servlet.http.Cookie; 4 | import javax.servlet.http.HttpServletRequest; 5 | import javax.servlet.http.HttpServletResponse; 6 | import org.springframework.http.HttpStatus; 7 | import org.springframework.web.bind.annotation.DeleteMapping; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | @RestController 12 | @RequestMapping("/tokens") 13 | public class TokenController 14 | { 15 | @DeleteMapping("/revoke") 16 | public void revoke(HttpServletRequest req, HttpServletResponse resp) 17 | { 18 | Cookie cookie = new Cookie("refreshToken", null); 19 | cookie.setHttpOnly(true); 20 | cookie.setSecure(false); // TODO: switch to true in production 21 | cookie.setPath(req.getContextPath() + "/oauth/token"); 22 | cookie.setMaxAge(0); 23 | 24 | resp.addCookie(cookie); 25 | resp.setStatus(HttpStatus.NO_CONTENT.value()); 26 | 27 | } 28 | } -------------------------------------------------------------------------------- /backend/imperblog-backend/src/main/java/com/aureocarmelino/imperblogbackend/api/exception/Field.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.api.exception; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | 6 | @AllArgsConstructor 7 | @Data 8 | public class Field 9 | { 10 | private String name; 11 | private String error; 12 | } 13 | -------------------------------------------------------------------------------- /backend/imperblog-backend/src/main/java/com/aureocarmelino/imperblogbackend/api/exception/GlobalExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.api.exception; 2 | 3 | import static com.aureocarmelino.imperblogbackend.util.constant.ConstantGlobalExceptionHandler.ERROR_ONE_OR_MORE_FILDS_ARE_INVALID; 4 | import java.time.OffsetDateTime; 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.context.MessageSource; 9 | import org.springframework.context.i18n.LocaleContextHolder; 10 | import org.springframework.http.HttpStatus; 11 | import org.springframework.http.ResponseEntity; 12 | import org.springframework.security.oauth2.common.exceptions.UnauthorizedUserException; 13 | import org.springframework.validation.FieldError; 14 | import org.springframework.validation.ObjectError; 15 | import org.springframework.web.bind.MethodArgumentNotValidException; 16 | import org.springframework.web.bind.annotation.ControllerAdvice; 17 | import org.springframework.web.bind.annotation.ExceptionHandler; 18 | import org.springframework.web.context.request.ServletWebRequest; 19 | import org.springframework.web.context.request.WebRequest; 20 | import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; 21 | import com.aureocarmelino.imperblogbackend.model.exception.BusinessException; 22 | import com.aureocarmelino.imperblogbackend.model.exception.ObjectNotFoundException; 23 | 24 | 25 | @ControllerAdvice 26 | public class GlobalExceptionHandler extends ResponseEntityExceptionHandler 27 | { 28 | @Autowired 29 | private MessageSource messageSource; 30 | 31 | @ExceptionHandler(UnauthorizedUserException.class) 32 | public ResponseEntity handleUnauthorizedClientException(UnauthorizedUserException ex, WebRequest request) 33 | { 34 | HttpStatus status = HttpStatus.UNAUTHORIZED; 35 | 36 | Problem problem = getProblem(status.value(), ex.getMessage(), null, request); 37 | 38 | return handleExceptionInternal(ex, problem, new org.springframework.http.HttpHeaders(), status, request); 39 | } 40 | 41 | 42 | @ExceptionHandler(NullPointerException.class) 43 | public ResponseEntity handleNullPointerException(NullPointerException ex, WebRequest request) 44 | { 45 | HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR; 46 | 47 | Problem problem = getProblem(status.value(), ex.getMessage(), null, request); 48 | 49 | return handleExceptionInternal(ex, problem, new org.springframework.http.HttpHeaders(), status, request); 50 | } 51 | 52 | @ExceptionHandler(ObjectNotFoundException.class) 53 | public ResponseEntity handleObjectNotFoundExceptionException(ObjectNotFoundException ex, WebRequest request) 54 | { 55 | HttpStatus status = HttpStatus.NOT_FOUND; 56 | 57 | Problem problem = getProblem(status.value(), ex.getMessage(), null, request); 58 | 59 | return handleExceptionInternal(ex, problem, new org.springframework.http.HttpHeaders(), status, request); 60 | } 61 | 62 | @ExceptionHandler(BusinessException.class) 63 | public ResponseEntity handleBusinessException(BusinessException ex, WebRequest request) 64 | { 65 | HttpStatus status = HttpStatus.BAD_REQUEST; 66 | 67 | Problem problem = getProblem(status.value(), ex.getMessage(), null,request); 68 | 69 | return handleExceptionInternal(ex, problem, new org.springframework.http.HttpHeaders(), status, request); 70 | } 71 | 72 | @Override 73 | protected ResponseEntity handleMethodArgumentNotValid(MethodArgumentNotValidException ex, 74 | org.springframework.http.HttpHeaders headers, HttpStatus status, WebRequest request) 75 | { 76 | 77 | List fieldErrors = new ArrayList(); 78 | 79 | for(ObjectError error : ex.getBindingResult().getAllErrors()) 80 | { 81 | String name = ((FieldError) error).getField(); 82 | String message = messageSource.getMessage(error, LocaleContextHolder.getLocale()); 83 | 84 | fieldErrors.add(new Field(name, message)); 85 | } 86 | 87 | Problem problem = getProblem( 88 | status.value(), 89 | ERROR_ONE_OR_MORE_FILDS_ARE_INVALID, 90 | fieldErrors, request); 91 | 92 | return handleExceptionInternal(ex, problem, headers, status, request); 93 | } 94 | 95 | private Problem getProblem(Integer status, String title, List fields, WebRequest request) 96 | { 97 | return new Problem(status, OffsetDateTime.now(), title, fields, ((ServletWebRequest)request).getRequest().getRequestURI().toString()); 98 | } 99 | } -------------------------------------------------------------------------------- /backend/imperblog-backend/src/main/java/com/aureocarmelino/imperblogbackend/api/exception/Problem.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.api.exception; 2 | 3 | import java.time.OffsetDateTime; 4 | import java.util.List; 5 | import com.fasterxml.jackson.annotation.JsonInclude; 6 | import com.fasterxml.jackson.annotation.JsonInclude.Include; 7 | import lombok.AllArgsConstructor; 8 | import lombok.Data; 9 | 10 | 11 | @Data 12 | @AllArgsConstructor 13 | @JsonInclude(value = Include.NON_NULL) 14 | public class Problem 15 | { 16 | private Integer status; 17 | private OffsetDateTime dateHours; 18 | private String title; 19 | private List fieldErrors; 20 | private String path; 21 | } 22 | -------------------------------------------------------------------------------- /backend/imperblog-backend/src/main/java/com/aureocarmelino/imperblogbackend/api/repository/AuthorRepository.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.api.repository; 2 | 3 | import java.util.List; 4 | import java.util.Optional; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | import org.springframework.data.jpa.repository.Query; 7 | import org.springframework.stereotype.Repository; 8 | import com.aureocarmelino.imperblogbackend.model.entity.Author; 9 | 10 | @Repository 11 | public interface AuthorRepository extends JpaRepository 12 | { 13 | Optional findByEmail(String email); 14 | 15 | Optional findByUsername(String username); 16 | 17 | @Query("SELECT author.gender, count(gender) FROM Author author group by author.gender") 18 | List findTotalAuthorsByGender(); 19 | } -------------------------------------------------------------------------------- /backend/imperblog-backend/src/main/java/com/aureocarmelino/imperblogbackend/api/repository/PostRepository.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.api.repository; 2 | 3 | import java.util.List; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.data.jpa.repository.Query; 6 | import org.springframework.stereotype.Repository; 7 | import com.aureocarmelino.imperblogbackend.model.entity.Post; 8 | import com.aureocarmelino.imperblogbackend.model.entity.Author; 9 | 10 | @Repository 11 | public interface PostRepository extends JpaRepository 12 | { 13 | @Query("SELECT post FROM Post post ORDER BY post.pkPost DESC") 14 | List findAll(); 15 | 16 | @Query("SELECT post FROM Post post WHERE post.author = :author ORDER BY post.pkPost DESC") 17 | List findAllAuthorPosts(Author author); 18 | } -------------------------------------------------------------------------------- /backend/imperblog-backend/src/main/java/com/aureocarmelino/imperblogbackend/api/service/AuthenticationService.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.api.service; 2 | 3 | import static com.aureocarmelino.imperblogbackend.util.constant.ConstantAuthentication.ERROR_EMAIL_UNAVAILABLE; 4 | import static com.aureocarmelino.imperblogbackend.util.constant.ConstantAuthentication.ERROR_INVALID_GENDER; 5 | import static com.aureocarmelino.imperblogbackend.util.constant.ConstantAuthentication.ERROR_USER_UNAVAILABLE; 6 | import java.util.Optional; 7 | import org.modelmapper.ModelMapper; 8 | import org.springframework.security.crypto.password.PasswordEncoder; 9 | import org.springframework.stereotype.Service; 10 | import org.springframework.transaction.annotation.Transactional; 11 | import com.aureocarmelino.imperblogbackend.api.repository.AuthorRepository; 12 | import com.aureocarmelino.imperblogbackend.model.dto.SignUpDto.SignUpDto; 13 | import com.aureocarmelino.imperblogbackend.model.entity.Author; 14 | import com.aureocarmelino.imperblogbackend.model.entity.Gender; 15 | import com.aureocarmelino.imperblogbackend.model.exception.BusinessException; 16 | import lombok.AllArgsConstructor; 17 | 18 | 19 | @AllArgsConstructor 20 | @Service 21 | public class AuthenticationService 22 | { 23 | private ModelMapper modelMapper; 24 | 25 | private AuthorRepository authorRepository; 26 | 27 | private PasswordEncoder passwordEncoder; 28 | 29 | 30 | @Transactional 31 | public Author signup(SignUpDto signUpDto) throws BusinessException 32 | { 33 | if(signUpDto.getGender().equals(Gender.F.getDescription()) 34 | || signUpDto.getGender().equals(Gender.M.getDescription())) 35 | { 36 | Optional checkEmail = this.authorRepository.findByEmail(signUpDto.getEmail()); 37 | 38 | Optional checkUsername = this.authorRepository.findByUsername(signUpDto.getUsername()); 39 | 40 | if(checkEmail.isPresent()) 41 | throw new BusinessException(ERROR_EMAIL_UNAVAILABLE); 42 | 43 | if(checkUsername.isPresent()) 44 | throw new BusinessException(ERROR_USER_UNAVAILABLE); 45 | 46 | Author newAuthor = this.modelMapper.map(signUpDto, Author.class); 47 | 48 | newAuthor.setPassword(passwordEncoder.encode(signUpDto.getPassword())); 49 | 50 | return this.authorRepository.save(newAuthor); 51 | } 52 | else 53 | { 54 | throw new BusinessException(ERROR_INVALID_GENDER); 55 | } 56 | 57 | } 58 | } -------------------------------------------------------------------------------- /backend/imperblog-backend/src/main/java/com/aureocarmelino/imperblogbackend/api/service/ChartService.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.api.service; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import org.springframework.stereotype.Service; 6 | import com.aureocarmelino.imperblogbackend.api.repository.AuthorRepository; 7 | import com.aureocarmelino.imperblogbackend.model.dto.chartDto.ChartDto; 8 | import lombok.AllArgsConstructor; 9 | 10 | @Service 11 | @AllArgsConstructor 12 | public class ChartService 13 | { 14 | private AuthorRepository authorRepository; 15 | 16 | public List findTotalAuthorsByGender() 17 | { 18 | List list = this.authorRepository.findTotalAuthorsByGender(); 19 | 20 | List listChartDto = new ArrayList<>(); 21 | 22 | for(Object[] elements: list) 23 | { 24 | String gender = String.valueOf(elements[0]); 25 | Long total = Long.valueOf(String.valueOf(elements[1])); 26 | 27 | listChartDto.add(new ChartDto(gender,total,selectColorByGender(gender))); 28 | } 29 | return listChartDto; 30 | } 31 | 32 | public String selectColorByGender(String gender) 33 | { 34 | // green: #1abc9c 35 | // rose: #ffcbdb 36 | 37 | return gender.equals("M") ? "#1abc9c" : "#ffcbdb"; 38 | } 39 | } -------------------------------------------------------------------------------- /backend/imperblog-backend/src/main/java/com/aureocarmelino/imperblogbackend/api/service/PostService.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.api.service; 2 | 3 | import static com.aureocarmelino.imperblogbackend.util.constant.ConstantPost.ERROR_POST_DOESNT_BELONG_LOGGED_USER; 4 | import static com.aureocarmelino.imperblogbackend.util.constant.ConstantPost.ERROR_POST_NOT_FOUND; 5 | import java.util.List; 6 | import org.modelmapper.ModelMapper; 7 | import org.springframework.beans.BeanUtils; 8 | import org.springframework.stereotype.Service; 9 | import org.springframework.transaction.annotation.Transactional; 10 | import com.aureocarmelino.imperblogbackend.api.repository.PostRepository; 11 | import com.aureocarmelino.imperblogbackend.model.dto.postDto.CreatePostDto; 12 | import com.aureocarmelino.imperblogbackend.model.entity.Post; 13 | import com.aureocarmelino.imperblogbackend.model.exception.BusinessException; 14 | import com.aureocarmelino.imperblogbackend.model.exception.ObjectNotFoundException; 15 | import com.aureocarmelino.imperblogbackend.security.AuthorDetailsServiceImpl; 16 | import lombok.AllArgsConstructor; 17 | 18 | 19 | @AllArgsConstructor 20 | @Service 21 | public class PostService 22 | { 23 | private ModelMapper modelMapper; 24 | 25 | private PostRepository postRepository; 26 | 27 | private AuthorDetailsServiceImpl authorDetailsServiceImpl; 28 | 29 | @Transactional 30 | public Post create(CreatePostDto createPostDto) 31 | { 32 | Post newPost = modelMapper.map(createPostDto, Post.class); 33 | 34 | newPost.setAuthor(authorDetailsServiceImpl.logged()); 35 | return postRepository.save(newPost); 36 | } 37 | 38 | public List findAll() 39 | { 40 | return postRepository.findAll(); 41 | } 42 | 43 | public List findAllAuthorPosts() 44 | { 45 | return postRepository.findAllAuthorPosts(authorDetailsServiceImpl.logged()); 46 | } 47 | 48 | public Post findById(Long id) 49 | { 50 | return this.postRepository.findById(id).orElseThrow(() -> 51 | new ObjectNotFoundException(ERROR_POST_NOT_FOUND)); 52 | } 53 | 54 | 55 | @Transactional 56 | public Post edit(CreatePostDto createPostDto) 57 | { 58 | Post editedPost = this.findById(createPostDto.getPkPost()); 59 | 60 | BeanUtils.copyProperties(createPostDto, editedPost, "pkPost"); 61 | editedPost.setAuthor(authorDetailsServiceImpl.logged()); 62 | return postRepository.save(editedPost); 63 | } 64 | 65 | 66 | @Transactional 67 | public void deleteById(Long id) 68 | { 69 | Post post = findById(id); 70 | 71 | if(!post.getAuthor().equals(authorDetailsServiceImpl.logged())) 72 | throw new BusinessException(ERROR_POST_DOESNT_BELONG_LOGGED_USER); 73 | 74 | 75 | this.postRepository.deleteById(id); 76 | } 77 | } -------------------------------------------------------------------------------- /backend/imperblog-backend/src/main/java/com/aureocarmelino/imperblogbackend/config/CorsFilterConfig.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.config; 2 | 3 | import java.io.IOException; 4 | 5 | import javax.servlet.Filter; 6 | import javax.servlet.FilterChain; 7 | import javax.servlet.ServletException; 8 | import javax.servlet.ServletRequest; 9 | import javax.servlet.ServletResponse; 10 | import javax.servlet.http.HttpServletRequest; 11 | import javax.servlet.http.HttpServletResponse; 12 | 13 | import org.springframework.beans.factory.annotation.Value; 14 | import org.springframework.core.Ordered; 15 | import org.springframework.core.annotation.Order; 16 | import org.springframework.stereotype.Component; 17 | 18 | @Component 19 | @Order(Ordered.HIGHEST_PRECEDENCE) 20 | public class CorsFilterConfig implements Filter 21 | { 22 | @Value("${imperblog.allowedorigin}") 23 | private String allowedOrigin; 24 | 25 | @Override 26 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 27 | throws IOException, ServletException 28 | { 29 | HttpServletRequest newRequest = (HttpServletRequest) request; 30 | 31 | HttpServletResponse newResponse = (HttpServletResponse) response; 32 | 33 | newResponse.setHeader("Access-Control-Allow-Origin", allowedOrigin); 34 | newResponse.setHeader("Access-Control-Allow-Credentials", "true"); 35 | 36 | if ("OPTIONS".equalsIgnoreCase(newRequest.getMethod()) && allowedOrigin.equals(newRequest.getHeader("Origin"))) 37 | { 38 | newResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, DELETE, PUT, OPTIONS"); 39 | newResponse.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type, Accept"); 40 | newResponse.setHeader("Access-Control-Max-Age", "3600"); // one hour 41 | newResponse.setStatus(HttpServletResponse.SC_OK); 42 | } 43 | else 44 | { 45 | chain.doFilter(request, response); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /backend/imperblog-backend/src/main/java/com/aureocarmelino/imperblogbackend/config/ModelMapperConfig.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.config; 2 | 3 | import org.modelmapper.ModelMapper; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | @Configuration 8 | public class ModelMapperConfig 9 | { 10 | @Bean 11 | protected ModelMapper createModelMapper() 12 | { 13 | ModelMapper modelMapper = new ModelMapper(); 14 | return modelMapper; 15 | } 16 | } -------------------------------------------------------------------------------- /backend/imperblog-backend/src/main/java/com/aureocarmelino/imperblogbackend/config/PasswordEncoderConfig.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 6 | import org.springframework.security.crypto.password.PasswordEncoder; 7 | 8 | @Configuration 9 | public class PasswordEncoderConfig 10 | { 11 | @Bean 12 | public PasswordEncoder passwordEncoder() 13 | { 14 | return new BCryptPasswordEncoder(); 15 | } 16 | } -------------------------------------------------------------------------------- /backend/imperblog-backend/src/main/java/com/aureocarmelino/imperblogbackend/config/SecretKey.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.config; 2 | 3 | public class SecretKey 4 | { 5 | public static final String KEY_TEST = "3032885ba9cd6621bcc4e7d6b6c35c2b"; 6 | } 7 | -------------------------------------------------------------------------------- /backend/imperblog-backend/src/main/java/com/aureocarmelino/imperblogbackend/model/dto/SignUpDto/SignUpDto.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.model.dto.SignUpDto; 2 | 3 | import javax.validation.constraints.Email; 4 | import javax.validation.constraints.NotBlank; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Builder; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | 10 | @Builder 11 | @Data 12 | @AllArgsConstructor 13 | @NoArgsConstructor 14 | public class SignUpDto 15 | { 16 | @NotBlank(message = "Enter password") 17 | private String password; 18 | 19 | @NotBlank(message = "Enter email") 20 | @Email(message = "Enter a valid email") 21 | private String email; 22 | 23 | @NotBlank(message = "Enter username") 24 | private String username; 25 | 26 | @NotBlank(message = "Enter gender") 27 | private String gender; 28 | } -------------------------------------------------------------------------------- /backend/imperblog-backend/src/main/java/com/aureocarmelino/imperblogbackend/model/dto/chartDto/ChartDto.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.model.dto.chartDto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | 7 | @Builder 8 | @Data 9 | @AllArgsConstructor 10 | public class ChartDto 11 | { 12 | private String gender; 13 | private Long total; 14 | private String color; 15 | } 16 | -------------------------------------------------------------------------------- /backend/imperblog-backend/src/main/java/com/aureocarmelino/imperblogbackend/model/dto/loginDto/LoginDto.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.model.dto.loginDto; 2 | 3 | import javax.validation.constraints.NotBlank; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @AllArgsConstructor 10 | @NoArgsConstructor 11 | public class LoginDto 12 | { 13 | @NotBlank 14 | private String email; 15 | 16 | @NotBlank 17 | private String password; 18 | } -------------------------------------------------------------------------------- /backend/imperblog-backend/src/main/java/com/aureocarmelino/imperblogbackend/model/dto/postDto/CreatePostDto.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.model.dto.postDto; 2 | 3 | import javax.validation.constraints.NotBlank; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | @Builder 10 | @Data 11 | @AllArgsConstructor 12 | @NoArgsConstructor 13 | public class CreatePostDto 14 | { 15 | private Long pkPost; 16 | 17 | @NotBlank(message = "Enter post content") 18 | private String postContent; 19 | 20 | @NotBlank(message = "Enter title content") 21 | private String title; 22 | } 23 | -------------------------------------------------------------------------------- /backend/imperblog-backend/src/main/java/com/aureocarmelino/imperblogbackend/model/entity/Author.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.model.entity; 2 | 3 | import java.time.LocalDateTime; 4 | import javax.persistence.Entity; 5 | import javax.persistence.EnumType; 6 | import javax.persistence.Enumerated; 7 | import javax.persistence.GeneratedValue; 8 | import javax.persistence.GenerationType; 9 | import javax.persistence.Id; 10 | import javax.persistence.Table; 11 | import javax.validation.constraints.Email; 12 | import javax.validation.constraints.NotBlank; 13 | import org.hibernate.annotations.CreationTimestamp; 14 | import org.hibernate.annotations.UpdateTimestamp; 15 | import com.fasterxml.jackson.annotation.JsonProperty; 16 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 17 | import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; 18 | import lombok.AllArgsConstructor; 19 | import lombok.Builder; 20 | import lombok.Data; 21 | import lombok.EqualsAndHashCode; 22 | import lombok.NoArgsConstructor; 23 | 24 | @Builder 25 | @Data 26 | @Table(name = "tb_author") 27 | @Entity 28 | @AllArgsConstructor 29 | @NoArgsConstructor 30 | @EqualsAndHashCode(onlyExplicitlyIncluded = true) 31 | public class Author 32 | { 33 | @Id 34 | @EqualsAndHashCode.Include 35 | @GeneratedValue(strategy = GenerationType.IDENTITY) 36 | private Long pkAuthor; 37 | 38 | @NotBlank 39 | @Email 40 | private String email; 41 | 42 | @NotBlank 43 | @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) 44 | private String password; 45 | 46 | @NotBlank 47 | private String username; 48 | 49 | @Enumerated(EnumType.STRING) 50 | private Gender gender; 51 | 52 | @JsonSerialize(using = LocalDateTimeSerializer.class) 53 | @CreationTimestamp 54 | private LocalDateTime creationDate; 55 | 56 | @JsonSerialize(using = LocalDateTimeSerializer.class) 57 | @UpdateTimestamp 58 | private LocalDateTime updateDate; 59 | } -------------------------------------------------------------------------------- /backend/imperblog-backend/src/main/java/com/aureocarmelino/imperblogbackend/model/entity/Gender.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.model.entity; 2 | 3 | import javax.validation.constraints.NotBlank; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | 7 | @Getter 8 | @AllArgsConstructor 9 | @NotBlank 10 | public enum Gender 11 | { 12 | M(1,"M"), 13 | F(2,"F"); 14 | 15 | private Integer pkGender; 16 | private String description; 17 | } -------------------------------------------------------------------------------- /backend/imperblog-backend/src/main/java/com/aureocarmelino/imperblogbackend/model/entity/Post.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.model.entity; 2 | 3 | import java.time.LocalDateTime; 4 | 5 | import javax.persistence.Entity; 6 | import javax.persistence.GeneratedValue; 7 | import javax.persistence.GenerationType; 8 | import javax.persistence.Id; 9 | import javax.persistence.JoinColumn; 10 | import javax.persistence.OneToOne; 11 | import javax.persistence.Table; 12 | 13 | import org.hibernate.annotations.CreationTimestamp; 14 | import org.hibernate.annotations.UpdateTimestamp; 15 | 16 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 17 | import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; 18 | 19 | import lombok.AllArgsConstructor; 20 | import lombok.Builder; 21 | import lombok.Data; 22 | import lombok.EqualsAndHashCode; 23 | import lombok.NoArgsConstructor; 24 | 25 | @Builder 26 | @Data 27 | @Table(name = "tb_post") 28 | @Entity 29 | @AllArgsConstructor 30 | @NoArgsConstructor 31 | @EqualsAndHashCode(onlyExplicitlyIncluded = true) 32 | public class Post 33 | { 34 | @Id 35 | @EqualsAndHashCode.Include 36 | @GeneratedValue(strategy = GenerationType.IDENTITY) 37 | private Long pkPost; 38 | 39 | @OneToOne 40 | @JoinColumn(name = "fk_author") 41 | private Author author; 42 | 43 | private String postContent; 44 | 45 | private String title; 46 | 47 | @JsonSerialize(using = LocalDateTimeSerializer.class) 48 | @CreationTimestamp 49 | private LocalDateTime creationDate; 50 | 51 | @JsonSerialize(using = LocalDateTimeSerializer.class) 52 | @UpdateTimestamp 53 | private LocalDateTime updateDate; 54 | } -------------------------------------------------------------------------------- /backend/imperblog-backend/src/main/java/com/aureocarmelino/imperblogbackend/model/exception/BusinessException.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.model.exception; 2 | 3 | public class BusinessException extends RuntimeException 4 | { 5 | private static final long serialVersionUID = 1L; 6 | 7 | public BusinessException(String message) 8 | { 9 | super(message); 10 | } 11 | } -------------------------------------------------------------------------------- /backend/imperblog-backend/src/main/java/com/aureocarmelino/imperblogbackend/model/exception/ObjectNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.model.exception; 2 | 3 | public class ObjectNotFoundException extends RuntimeException 4 | { 5 | private static final long serialVersionUID = 1L; 6 | 7 | public ObjectNotFoundException(String message) 8 | { 9 | super(message); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /backend/imperblog-backend/src/main/java/com/aureocarmelino/imperblogbackend/security/AuthorDetailsServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.security; 2 | 3 | import static com.aureocarmelino.imperblogbackend.util.constant.ConstantAuthentication.ERROR_EMAIL_NOT_FOUND; 4 | import static com.aureocarmelino.imperblogbackend.util.constant.ConstantAuthentication.ERROR_USERNAME_OR_PASSWORD_INVALID; 5 | import java.util.Collection; 6 | import java.util.HashSet; 7 | import java.util.Map; 8 | import java.util.Optional; 9 | import java.util.Set; 10 | import javax.servlet.http.HttpServletRequest; 11 | import org.modelmapper.ModelMapper; 12 | import org.springframework.security.core.GrantedAuthority; 13 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 14 | import org.springframework.security.core.userdetails.UserDetails; 15 | import org.springframework.security.core.userdetails.UserDetailsService; 16 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 17 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 18 | import org.springframework.security.crypto.password.PasswordEncoder; 19 | import org.springframework.stereotype.Service; 20 | import org.springframework.web.context.request.RequestContextHolder; 21 | import org.springframework.web.context.request.ServletRequestAttributes; 22 | import com.aureocarmelino.imperblogbackend.api.repository.AuthorRepository; 23 | import com.aureocarmelino.imperblogbackend.model.entity.Author; 24 | import com.aureocarmelino.imperblogbackend.model.exception.BusinessException; 25 | import com.fasterxml.jackson.core.type.TypeReference; 26 | import com.fasterxml.jackson.databind.ObjectMapper; 27 | 28 | import lombok.RequiredArgsConstructor; 29 | 30 | 31 | @RequiredArgsConstructor 32 | @Service 33 | public class AuthorDetailsServiceImpl implements UserDetailsService 34 | { 35 | private final AuthorRepository authorRepository; 36 | 37 | private final ModelMapper modelMapper; 38 | 39 | private Map jsonMap; 40 | 41 | @Override 42 | public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException , BusinessException 43 | { 44 | Optional authorOptional = authorRepository.findByEmail(email); 45 | 46 | Author author = authorOptional.orElseThrow(()-> new UsernameNotFoundException(ERROR_USERNAME_OR_PASSWORD_INVALID)); 47 | 48 | return new AuthorSystem(author , getPermission(author)); 49 | } 50 | 51 | private Collection getPermission(Author author) 52 | { 53 | Set authorities = new HashSet<>(); 54 | 55 | authorities.add(new SimpleGrantedAuthority("AUTHORITIES")); 56 | 57 | return authorities; 58 | } 59 | 60 | public Author findByEmail(String email) throws BusinessException 61 | { 62 | return this.authorRepository.findByEmail(email).orElseThrow(() -> 63 | new BusinessException(ERROR_EMAIL_NOT_FOUND)); 64 | } 65 | 66 | public Author logged() throws BusinessException 67 | { 68 | HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest(); 69 | String token = request.getHeader("Authorization").split(" ")[1]; 70 | 71 | 72 | java.util.Base64.Decoder decoder = java.util.Base64.getUrlDecoder(); 73 | String[] parts = token.split("\\."); // split out the "parts" (header, payload and signature) 74 | String payloadJson = new String(decoder.decode(parts[1])); 75 | 76 | 77 | Map jsonMap = convertJsonToMap(payloadJson); 78 | Author author = modelMapper.map(jsonMap.get("logged"), Author.class); 79 | 80 | return author; 81 | } 82 | 83 | public Map convertJsonToMap(String jsonString) 84 | { 85 | ObjectMapper objectMapper = new ObjectMapper(); 86 | 87 | try 88 | { 89 | this.jsonMap = objectMapper.readValue(jsonString,new TypeReference>() {}); 90 | return this.jsonMap; 91 | } 92 | catch (Exception ex) 93 | { 94 | throw new RuntimeException(ex); 95 | } 96 | } 97 | 98 | public String passwordEncode(String palavraPasse) 99 | { 100 | PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); 101 | return passwordEncoder.encode(palavraPasse); 102 | } 103 | 104 | } -------------------------------------------------------------------------------- /backend/imperblog-backend/src/main/java/com/aureocarmelino/imperblogbackend/security/AuthorSystem.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.security; 2 | 3 | import java.util.Collection; 4 | import org.springframework.security.core.GrantedAuthority; 5 | import org.springframework.security.core.userdetails.User; 6 | import com.aureocarmelino.imperblogbackend.model.entity.Author; 7 | import lombok.Getter; 8 | 9 | @Getter 10 | public class AuthorSystem extends User 11 | { 12 | private static final long serialVersionUID = 1L; 13 | private Author author; 14 | 15 | public AuthorSystem(Author author, Collection authorities) 16 | { 17 | super(author.getEmail(), author.getPassword(), authorities); 18 | this.author = author; 19 | } 20 | } -------------------------------------------------------------------------------- /backend/imperblog-backend/src/main/java/com/aureocarmelino/imperblogbackend/security/oauth2/AuthorizationServerConfig.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.security.oauth2; 2 | 3 | import java.util.Arrays; 4 | 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.security.authentication.AuthenticationManager; 8 | import org.springframework.security.core.userdetails.UserDetailsService; 9 | import org.springframework.security.crypto.password.PasswordEncoder; 10 | import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; 11 | import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; 12 | import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; 13 | import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; 14 | import org.springframework.security.oauth2.provider.token.TokenEnhancer; 15 | import org.springframework.security.oauth2.provider.token.TokenEnhancerChain; 16 | import org.springframework.security.oauth2.provider.token.TokenStore; 17 | import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; 18 | import org.springframework.security.oauth2.provider.token.store.JwtTokenStore; 19 | 20 | import com.aureocarmelino.imperblogbackend.config.SecretKey; 21 | import com.aureocarmelino.imperblogbackend.security.token.CustomTokenEncher; 22 | 23 | import lombok.AllArgsConstructor; 24 | 25 | @AllArgsConstructor 26 | @Configuration 27 | @EnableAuthorizationServer 28 | public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter 29 | { 30 | private AuthenticationManager authenticationManager; 31 | 32 | private PasswordEncoder passwordEncoder; 33 | 34 | private UserDetailsService userDetailsService; 35 | 36 | 37 | @Override 38 | public void configure(ClientDetailsServiceConfigurer clients) throws Exception 39 | { 40 | clients.inMemory() 41 | .withClient("teste") 42 | .secret(passwordEncoder.encode("teste")) 43 | .scopes("read", "write") 44 | .authorizedGrantTypes("password", "refresh_token") 45 | .accessTokenValiditySeconds(3600) 46 | .refreshTokenValiditySeconds(3600*2); 47 | } 48 | 49 | @Override 50 | public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception 51 | { 52 | TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain(); 53 | 54 | tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer(), accessTokenConverter())); 55 | 56 | endpoints 57 | .tokenStore(tokenStore()) 58 | .tokenEnhancer(tokenEnhancerChain) 59 | .reuseRefreshTokens(false) 60 | .userDetailsService(userDetailsService) 61 | .authenticationManager(authenticationManager); 62 | } 63 | 64 | @Bean 65 | public JwtAccessTokenConverter accessTokenConverter() 66 | { 67 | JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter(); 68 | 69 | accessTokenConverter.setSigningKey(SecretKey.KEY_TEST); 70 | accessTokenConverter.setVerifierKey(SecretKey.KEY_TEST); 71 | 72 | return accessTokenConverter; 73 | } 74 | 75 | @Bean 76 | public TokenStore tokenStore() 77 | { 78 | return new JwtTokenStore(accessTokenConverter()); 79 | } 80 | 81 | @Bean 82 | public TokenEnhancer tokenEnhancer() 83 | { 84 | return new CustomTokenEncher(); 85 | } 86 | } -------------------------------------------------------------------------------- /backend/imperblog-backend/src/main/java/com/aureocarmelino/imperblogbackend/security/oauth2/ResourceServerConfig.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.security.oauth2; 2 | 3 | import java.util.Collection; 4 | import java.util.Collections; 5 | import java.util.List; 6 | import java.util.stream.Collectors; 7 | 8 | import javax.crypto.spec.SecretKeySpec; 9 | 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.context.annotation.Configuration; 12 | import org.springframework.http.HttpMethod; 13 | import org.springframework.security.authentication.AuthenticationManager; 14 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 15 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; 16 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 17 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 18 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 19 | import org.springframework.security.config.http.SessionCreationPolicy; 20 | import org.springframework.security.core.GrantedAuthority; 21 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 22 | import org.springframework.security.core.userdetails.UserDetailsService; 23 | import org.springframework.security.oauth2.jwt.JwtDecoder; 24 | import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; 25 | import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter; 26 | import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter; 27 | 28 | import com.aureocarmelino.imperblogbackend.config.SecretKey; 29 | 30 | import lombok.AllArgsConstructor; 31 | 32 | 33 | @AllArgsConstructor 34 | @Configuration 35 | @EnableWebSecurity 36 | @EnableGlobalMethodSecurity(prePostEnabled = true) 37 | public class ResourceServerConfig extends WebSecurityConfigurerAdapter 38 | { 39 | private UserDetailsService userDetailsService; 40 | 41 | 42 | @Override 43 | protected void configure(AuthenticationManagerBuilder auth) throws Exception 44 | { 45 | auth.userDetailsService(userDetailsService); 46 | } 47 | 48 | 49 | @Override 50 | protected void configure(HttpSecurity http) throws Exception 51 | { 52 | http.authorizeRequests() 53 | .antMatchers(HttpMethod.POST,"/oauth/token").permitAll() 54 | .antMatchers(HttpMethod.POST,"/api/auth/signup").permitAll() 55 | .anyRequest().authenticated() 56 | .and() 57 | .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) 58 | .and() 59 | .csrf().disable() 60 | .oauth2ResourceServer().jwt().jwtAuthenticationConverter(jwtAuthenticationConverter()); 61 | } 62 | 63 | 64 | @Bean 65 | public JwtDecoder jwtDecoder() 66 | { 67 | var secretKey = new SecretKeySpec(SecretKey.KEY_TEST.getBytes(), "HmacSHA256"); 68 | 69 | return NimbusJwtDecoder.withSecretKey(secretKey).build(); 70 | } 71 | 72 | 73 | @Bean 74 | @Override 75 | protected AuthenticationManager authenticationManager() throws Exception 76 | { 77 | return super.authenticationManager(); 78 | } 79 | 80 | private JwtAuthenticationConverter jwtAuthenticationConverter() 81 | { 82 | JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter(); 83 | 84 | jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwt -> 85 | { 86 | List authorities = jwt.getClaimAsStringList("authorities"); 87 | 88 | if (authorities == null) 89 | { 90 | authorities = Collections.emptyList(); 91 | } 92 | 93 | JwtGrantedAuthoritiesConverter scopesAuthoritiesConverter = new JwtGrantedAuthoritiesConverter(); 94 | 95 | Collection grantedAuthorities = scopesAuthoritiesConverter.convert(jwt); 96 | 97 | grantedAuthorities.addAll(authorities.stream() 98 | .map(SimpleGrantedAuthority::new) 99 | .collect(Collectors.toList())); 100 | 101 | return grantedAuthorities; 102 | }); 103 | 104 | return jwtAuthenticationConverter; 105 | } 106 | } -------------------------------------------------------------------------------- /backend/imperblog-backend/src/main/java/com/aureocarmelino/imperblogbackend/security/token/CustomTokenEncher.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.security.token; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; 6 | import org.springframework.security.oauth2.common.OAuth2AccessToken; 7 | import org.springframework.security.oauth2.provider.OAuth2Authentication; 8 | import org.springframework.security.oauth2.provider.token.TokenEnhancer; 9 | import com.aureocarmelino.imperblogbackend.security.AuthorSystem; 10 | 11 | public class CustomTokenEncher implements TokenEnhancer 12 | { 13 | @Override 14 | public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) 15 | { 16 | AuthorSystem authorSystem = (AuthorSystem) authentication.getPrincipal(); 17 | 18 | Map info = new HashMap<>(); 19 | 20 | info.put("logged", authorSystem.getAuthor()); 21 | 22 | ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(info); 23 | 24 | return accessToken; 25 | } 26 | } -------------------------------------------------------------------------------- /backend/imperblog-backend/src/main/java/com/aureocarmelino/imperblogbackend/security/token/RefreshTokenCookiePreProcessorFilter.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.security.token; 2 | 3 | import java.io.IOException; 4 | import java.util.Map; 5 | import java.util.stream.Stream; 6 | import javax.servlet.Filter; 7 | import javax.servlet.FilterChain; 8 | import javax.servlet.ServletException; 9 | import javax.servlet.ServletRequest; 10 | import javax.servlet.ServletResponse; 11 | import javax.servlet.http.HttpServletRequest; 12 | import javax.servlet.http.HttpServletRequestWrapper; 13 | import org.apache.catalina.util.ParameterMap; 14 | import org.springframework.core.Ordered; 15 | import org.springframework.core.annotation.Order; 16 | import org.springframework.stereotype.Component; 17 | 18 | @Component 19 | @Order(Ordered.HIGHEST_PRECEDENCE) 20 | public class RefreshTokenCookiePreProcessorFilter implements Filter 21 | { 22 | 23 | @Override 24 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 25 | throws IOException, ServletException 26 | { 27 | HttpServletRequest req = (HttpServletRequest) request; 28 | 29 | if ("/oauth/token".equalsIgnoreCase(req.getRequestURI()) 30 | && "refresh_token".equals(req.getParameter("grant_type")) 31 | && req.getCookies() != null) { 32 | 33 | String refreshToken = 34 | Stream.of(req.getCookies()) 35 | .filter(cookie -> "refreshToken".equals(cookie.getName())) 36 | .findFirst() 37 | .map(cookie -> cookie.getValue()) 38 | .orElse(null); 39 | 40 | req = new MyServletRequestWrapper(req, refreshToken); 41 | } 42 | 43 | chain.doFilter(req, response); 44 | } 45 | 46 | static class MyServletRequestWrapper extends HttpServletRequestWrapper 47 | { 48 | private String refreshToken; 49 | 50 | public MyServletRequestWrapper(HttpServletRequest request, String refreshToken) 51 | { 52 | super(request); 53 | this.refreshToken = refreshToken; 54 | } 55 | 56 | @Override 57 | public Map getParameterMap() 58 | { 59 | ParameterMap map = new ParameterMap<>(getRequest().getParameterMap()); 60 | map.put("refresh_token", new String[] { refreshToken }); 61 | map.setLocked(true); 62 | return map; 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /backend/imperblog-backend/src/main/java/com/aureocarmelino/imperblogbackend/security/token/RefreshTokenPostProcessor.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.security.token; 2 | 3 | import javax.servlet.http.Cookie; 4 | import javax.servlet.http.HttpServletRequest; 5 | import javax.servlet.http.HttpServletResponse; 6 | import org.springframework.core.MethodParameter; 7 | import org.springframework.http.MediaType; 8 | import org.springframework.http.converter.HttpMessageConverter; 9 | import org.springframework.http.server.ServerHttpRequest; 10 | import org.springframework.http.server.ServerHttpResponse; 11 | import org.springframework.http.server.ServletServerHttpRequest; 12 | import org.springframework.http.server.ServletServerHttpResponse; 13 | import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; 14 | import org.springframework.security.oauth2.common.OAuth2AccessToken; 15 | import org.springframework.web.bind.annotation.ControllerAdvice; 16 | import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; 17 | 18 | @ControllerAdvice 19 | public class RefreshTokenPostProcessor implements ResponseBodyAdvice 20 | { 21 | @Override 22 | public boolean supports(MethodParameter returnType, Class> converterType) 23 | { 24 | return returnType.getMethod().getName().equals("postAccessToken"); 25 | } 26 | 27 | @Override 28 | public OAuth2AccessToken beforeBodyWrite(OAuth2AccessToken body, MethodParameter returnType, 29 | MediaType selectedContentType, Class> selectedConverterType, 30 | ServerHttpRequest request, ServerHttpResponse response) 31 | { 32 | 33 | HttpServletRequest req = ((ServletServerHttpRequest) request).getServletRequest(); 34 | HttpServletResponse resp = ((ServletServerHttpResponse) response).getServletResponse(); 35 | 36 | DefaultOAuth2AccessToken token = (DefaultOAuth2AccessToken) body; 37 | 38 | String refreshToken = body.getRefreshToken().getValue(); 39 | addRefreshTokenToCookie(refreshToken, req, resp); 40 | removeRefreshTokenToBody(token); 41 | 42 | return body; 43 | } 44 | 45 | private void removeRefreshTokenToBody(DefaultOAuth2AccessToken token) 46 | { 47 | token.setRefreshToken(null); 48 | } 49 | 50 | private void addRefreshTokenToCookie(String refreshToken, HttpServletRequest req, HttpServletResponse resp) 51 | { 52 | Cookie refreshTokenCookie = new Cookie("refreshToken", refreshToken); 53 | refreshTokenCookie.setHttpOnly(true); 54 | refreshTokenCookie.setSecure(false); // TODO: switch to true in production 55 | refreshTokenCookie.setPath(req.getContextPath() + "/oauth/token"); 56 | refreshTokenCookie.setMaxAge(2592000); 57 | resp.addCookie(refreshTokenCookie); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /backend/imperblog-backend/src/main/java/com/aureocarmelino/imperblogbackend/util/constant/ConstantAuthentication.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.util.constant; 2 | 3 | public class ConstantAuthentication 4 | { 5 | public static final String ERROR_EMAIL_UNAVAILABLE = "Email unavailable"; 6 | 7 | public static final String ERROR_USER_UNAVAILABLE = "Username unavailable"; 8 | 9 | public static final String ERROR_INVALID_GENDER = "Invalid gender"; 10 | 11 | public static final String ERROR_USERNAME_OR_PASSWORD_INVALID = "Username or password is invalid"; 12 | 13 | public static final String ERROR_EMAIL_NOT_FOUND = "Email not found"; 14 | } -------------------------------------------------------------------------------- /backend/imperblog-backend/src/main/java/com/aureocarmelino/imperblogbackend/util/constant/ConstantGlobalExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.util.constant; 2 | 3 | public class ConstantGlobalExceptionHandler 4 | { 5 | public static final String ERROR_ONE_OR_MORE_FILDS_ARE_INVALID = "One or more fields are invalid. Please fill in correctly and try again"; 6 | } 7 | -------------------------------------------------------------------------------- /backend/imperblog-backend/src/main/java/com/aureocarmelino/imperblogbackend/util/constant/ConstantPost.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.util.constant; 2 | 3 | public class ConstantPost 4 | { 5 | public static final String ERROR_POST_NOT_FOUND = "Post not found"; 6 | 7 | public static final String ERROR_POST_DOESNT_BELONG_LOGGED_USER = "Post doesn't belong to the logged user"; 8 | } 9 | -------------------------------------------------------------------------------- /backend/imperblog-backend/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.datasource.url=jdbc:postgresql://127.0.0.1:5432/imperblog_bd?useSSL=false&useTimezone=true&serverTimezone=Africa/Luanda 2 | spring.datasource.username=postgres 3 | spring.datasource.password=postgres 4 | 5 | spring.jpa.show-sql=true 6 | spring.jpa.hibernate.ddl-auto=none 7 | server.port=8080 8 | 9 | spring.mvc.pathmatch.matching-strategy=ant-path-matcher 10 | spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=http://127.0.0.1:8080/oauth/check_token 11 | spring.security.oauth2.resourceserver.opaquetoken.client-id=teste 12 | spring.security.oauth2.resourceserver.opaquetoken.client-secret=teste 13 | spring.jackson.date-format=dd-MM-yyyy 14 | 15 | imperblog.allowedorigin=http://localhost:4200 -------------------------------------------------------------------------------- /backend/imperblog-backend/src/main/resources/db/migration/V01__create_table_author.sql: -------------------------------------------------------------------------------- 1 | /* PostgreSQL */ 2 | 3 | create table tb_author 4 | ( 5 | pk_author serial PRIMARY KEY, 6 | email VARCHAR(250) UNIQUE NOT NULL, 7 | password VARCHAR(250) NOT NULL, 8 | username VARCHAR(250) UNIQUE NOT NULL, 9 | gender VARCHAR(20) NOT NULL, 10 | creation_date TIMESTAMP NOT NULL, 11 | update_date TIMESTAMP 12 | ); -------------------------------------------------------------------------------- /backend/imperblog-backend/src/main/resources/db/migration/V02__create_table_post.sql: -------------------------------------------------------------------------------- 1 | /* PostgreSQL */ 2 | 3 | create table tb_post 4 | ( 5 | pk_post serial PRIMARY KEY, 6 | fk_author INTEGER NOT NULL, 7 | post_content TEXT NOT NULL, 8 | title VARCHAR(250) NOT NULL, 9 | creation_date TIMESTAMP NOT NULL, 10 | update_date TIMESTAMP, 11 | FOREIGN KEY (fk_author) REFERENCES tb_author(pk_author) 12 | ); -------------------------------------------------------------------------------- /backend/imperblog-backend/src/test/java/com/aureocarmelino/imperblogbackend/ImperblogBackendApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class ImperblogBackendApplicationTests 8 | { 9 | public static void main(String[] args) 10 | { 11 | SpringApplication.run(ImperblogBackendApplication.class, args); 12 | } 13 | } -------------------------------------------------------------------------------- /backend/imperblog-backend/src/test/java/com/aureocarmelino/imperblogbackend/api/controller/AuthenticationControllerTest.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.api.controller; 2 | 3 | 4 | import static org.mockito.ArgumentMatchers.*; 5 | import static org.junit.Assert.assertEquals; 6 | import static org.junit.Assert.assertNotNull; 7 | import org.junit.jupiter.api.BeforeEach; 8 | import org.junit.jupiter.api.Test; 9 | import org.mockito.InjectMocks; 10 | import org.mockito.Mock; 11 | import org.mockito.Mockito; 12 | import org.mockito.MockitoAnnotations; 13 | import org.springframework.boot.test.context.SpringBootTest; 14 | import org.springframework.http.HttpStatus; 15 | import org.springframework.http.ResponseEntity; 16 | import com.aureocarmelino.imperblogbackend.api.service.AuthenticationService; 17 | import com.aureocarmelino.imperblogbackend.model.dto.SignUpDto.SignUpDto; 18 | import com.aureocarmelino.imperblogbackend.model.entity.Author; 19 | import com.aureocarmelino.imperblogbackend.model.entity.Gender; 20 | 21 | 22 | 23 | @SpringBootTest 24 | class AuthenticationControllerTest 25 | { 26 | @Mock 27 | private AuthenticationService authenticationService; 28 | 29 | @InjectMocks 30 | private AuthenticationController authenticationController; 31 | 32 | private static final Long PK_AUTHOR = 1L; 33 | private static final String EMAIL = "aureo.roberto@hotmail.com"; 34 | private static final String PASSWORD = "mypassword"; 35 | private static final String USERNAME = "aureo.roberto"; 36 | private static final Gender GENDER = Gender.M; 37 | 38 | private SignUpDto signUpDto; 39 | private Author author; 40 | 41 | @BeforeEach 42 | void init() throws Exception 43 | { 44 | MockitoAnnotations.openMocks(this); 45 | startInstances(); 46 | } 47 | 48 | public void startInstances() 49 | { 50 | 51 | author = Author.builder() 52 | .pkAuthor(PK_AUTHOR) 53 | .email(EMAIL) 54 | .username(USERNAME) 55 | .password(PASSWORD) 56 | .gender(GENDER) 57 | .build(); 58 | 59 | signUpDto = SignUpDto.builder() 60 | .email(EMAIL) 61 | .password(PASSWORD) 62 | .username(USERNAME) 63 | .gender(GENDER.getDescription()) 64 | .build(); 65 | } 66 | 67 | @Test 68 | void whenSignupThenReturnCreated() 69 | { 70 | Mockito.when(authenticationService.signup(any())).thenReturn(author); 71 | 72 | ResponseEntity response = authenticationController.signup(signUpDto); 73 | 74 | assertEquals(HttpStatus.CREATED, response.getStatusCode()); 75 | assertNotNull(response.getHeaders().get("Location")); 76 | assertEquals(ResponseEntity.class, response.getClass()); 77 | } 78 | 79 | } -------------------------------------------------------------------------------- /backend/imperblog-backend/src/test/java/com/aureocarmelino/imperblogbackend/api/controller/ChartControllerTest.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.api.controller; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertNotNull; 5 | import java.util.List; 6 | import org.junit.jupiter.api.BeforeEach; 7 | import org.junit.jupiter.api.Test; 8 | import org.mockito.InjectMocks; 9 | import org.mockito.Mock; 10 | import org.mockito.Mockito; 11 | import org.mockito.MockitoAnnotations; 12 | import org.springframework.boot.test.context.SpringBootTest; 13 | import org.springframework.http.HttpStatus; 14 | import org.springframework.http.ResponseEntity; 15 | import com.aureocarmelino.imperblogbackend.api.service.ChartService; 16 | import com.aureocarmelino.imperblogbackend.model.dto.chartDto.ChartDto; 17 | import com.aureocarmelino.imperblogbackend.model.entity.Gender; 18 | 19 | 20 | @SpringBootTest 21 | class ChartControllerTest 22 | { 23 | @Mock 24 | private ChartService chartService; 25 | 26 | @InjectMocks 27 | private ChartController chartController; 28 | 29 | 30 | private static final Long TOTAL_MALE = 6L; 31 | private static final String MALE = Gender.M.getDescription(); 32 | 33 | private static final Long TOTAL_FEMALE = 7L; 34 | private static final String FEMALE = Gender.F.getDescription(); 35 | 36 | private ChartDto chartDtoMale; 37 | private ChartDto chartDtoFemale; 38 | 39 | 40 | @BeforeEach 41 | void init() throws Exception 42 | { 43 | MockitoAnnotations.openMocks(this); 44 | startInstances(); 45 | } 46 | 47 | public void startInstances() 48 | { 49 | 50 | chartDtoMale = ChartDto.builder() 51 | .gender(MALE) 52 | .total(TOTAL_MALE) 53 | .build(); 54 | 55 | chartDtoFemale = ChartDto.builder() 56 | .gender(FEMALE) 57 | .total(TOTAL_FEMALE) 58 | .build(); 59 | } 60 | 61 | 62 | 63 | 64 | @Test 65 | void whenFindTotalAuthorsByGenderThenReturnAnListOfChartDto() 66 | { 67 | Mockito.when(chartService.findTotalAuthorsByGender()).thenReturn(List.of(chartDtoMale,chartDtoFemale)); 68 | 69 | ResponseEntity> listChartDto = chartController.findTotalAuthorsByGender(); 70 | 71 | assertNotNull(listChartDto); 72 | assertEquals(2, listChartDto.getBody().size()); 73 | assertEquals(HttpStatus.OK, listChartDto.getStatusCode()); 74 | assertEquals(ResponseEntity.class, listChartDto.getClass()); 75 | } 76 | } -------------------------------------------------------------------------------- /backend/imperblog-backend/src/test/java/com/aureocarmelino/imperblogbackend/api/controller/PostControllerTest.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.api.controller; 2 | 3 | 4 | import static org.junit.Assert.assertEquals; 5 | import static org.junit.Assert.assertNotNull; 6 | import static org.mockito.ArgumentMatchers.any; 7 | import static org.mockito.ArgumentMatchers.anyLong; 8 | import static org.mockito.Mockito.doNothing; 9 | import static org.mockito.Mockito.times; 10 | import static org.mockito.Mockito.verify; 11 | import static org.mockito.Mockito.when; 12 | import java.util.List; 13 | import org.junit.jupiter.api.Assertions; 14 | import org.junit.jupiter.api.BeforeEach; 15 | import org.junit.jupiter.api.Test; 16 | import org.mockito.InjectMocks; 17 | import org.mockito.Mock; 18 | import org.mockito.Mockito; 19 | import org.mockito.MockitoAnnotations; 20 | import org.springframework.boot.test.context.SpringBootTest; 21 | import org.springframework.http.HttpStatus; 22 | import org.springframework.http.ResponseEntity; 23 | import com.aureocarmelino.imperblogbackend.api.service.PostService; 24 | import com.aureocarmelino.imperblogbackend.model.dto.postDto.CreatePostDto; 25 | import com.aureocarmelino.imperblogbackend.model.entity.Author; 26 | import com.aureocarmelino.imperblogbackend.model.entity.Gender; 27 | import com.aureocarmelino.imperblogbackend.model.entity.Post; 28 | 29 | 30 | @SpringBootTest 31 | class PostControllerTest 32 | { 33 | @Mock 34 | private PostService postService; 35 | 36 | @InjectMocks 37 | private PostController postController; 38 | 39 | private static final int INDEX = 0; 40 | 41 | private static final Long PK_POST = 1L; 42 | private static final String TITLE = "My new post"; 43 | private static final String EDITED_TITLE = "EDITED TITLE"; 44 | 45 | private static final String POST_CONTENT = "This is my new post content"; 46 | 47 | private static final Long PK_AUTHOR = 1L; 48 | private static final String EMAIL = "aureo.roberto@hotmail.com"; 49 | private static final String PASSWORD = "mypassword"; 50 | private static final String USERNAME = "aureo.roberto"; 51 | private static final Gender GENDER = Gender.M; 52 | 53 | private Post post, editedPost; 54 | private Author author; 55 | private CreatePostDto createPostDto; 56 | 57 | 58 | @BeforeEach 59 | void init() throws Exception 60 | { 61 | MockitoAnnotations.openMocks(this); 62 | startInstances(); 63 | } 64 | 65 | public void startInstances() 66 | { 67 | author = Author.builder() 68 | .pkAuthor(PK_AUTHOR) 69 | .email(EMAIL) 70 | .username(USERNAME) 71 | .password(PASSWORD) 72 | .gender(GENDER) 73 | .build(); 74 | 75 | post = Post.builder() 76 | .pkPost(PK_POST) 77 | .title(TITLE) 78 | .postContent(POST_CONTENT) 79 | .author(author) 80 | .build(); 81 | 82 | 83 | createPostDto = CreatePostDto.builder() 84 | .pkPost(PK_POST) 85 | .title(TITLE) 86 | .postContent(POST_CONTENT) 87 | .build(); 88 | 89 | editedPost = Post.builder() 90 | .pkPost(PK_POST) 91 | .title(EDITED_TITLE) 92 | .postContent(POST_CONTENT) 93 | .author(author) 94 | .build(); 95 | } 96 | 97 | 98 | @Test 99 | void whenCreatePostThenReturnAnPost() 100 | { 101 | when(postService.create(any())).thenReturn(post); 102 | 103 | ResponseEntity newPost = postController.createPost(createPostDto); 104 | 105 | assertNotNull(newPost); 106 | assertEquals(HttpStatus.CREATED, newPost.getStatusCode()); 107 | assertNotNull(newPost.getHeaders().get("Location")); 108 | assertEquals(ResponseEntity.class, newPost.getClass()); 109 | } 110 | 111 | 112 | @Test 113 | void whenEditPostThenReturnAnEditedPost() 114 | { 115 | when(postService.edit(any())).thenReturn(editedPost); 116 | 117 | ResponseEntity editedPost = postController.editPost(createPostDto); 118 | 119 | assertNotNull(editedPost); 120 | assertEquals(EDITED_TITLE, editedPost.getBody().getTitle()); 121 | assertEquals(HttpStatus.OK, editedPost.getStatusCode()); 122 | assertEquals(ResponseEntity.class, editedPost.getClass()); 123 | } 124 | 125 | @Test 126 | void whenFindAllThenReturnListOfPost() 127 | { 128 | when(postService.findAll()).thenReturn(List.of(post)); 129 | 130 | ResponseEntity> posts = postController.findAll(); 131 | 132 | Assertions.assertNotNull(posts); 133 | Assertions.assertEquals(HttpStatus.OK, posts.getStatusCode()); 134 | Assertions.assertEquals(author, posts.getBody().get(INDEX).getAuthor()); 135 | Assertions.assertEquals(Author.class, posts.getBody().get(INDEX).getAuthor().getClass()); 136 | Assertions.assertEquals(Post.class, posts.getBody().get(INDEX).getClass()); 137 | Assertions.assertEquals(PK_POST, posts.getBody().get(INDEX).getPkPost()); 138 | Assertions.assertEquals(TITLE, posts.getBody().get(INDEX).getTitle()); 139 | Assertions.assertEquals(POST_CONTENT, posts.getBody().get(INDEX).getPostContent()); 140 | } 141 | 142 | 143 | @Test 144 | void whenFindAllAuthorPostsThenReturnAnListOfAuthorPosts() 145 | { 146 | Mockito.when(postService.findAllAuthorPosts()).thenReturn(List.of(post)); 147 | 148 | ResponseEntity> posts = postController.findAllAuthorPosts(); 149 | 150 | Assertions.assertNotNull(posts); 151 | Assertions.assertEquals(HttpStatus.OK, posts.getStatusCode()); 152 | Assertions.assertEquals(author, posts.getBody().get(INDEX).getAuthor()); 153 | Assertions.assertEquals(Author.class, posts.getBody().get(INDEX).getAuthor().getClass()); 154 | Assertions.assertEquals(Post.class, posts.getBody().get(INDEX).getClass()); 155 | Assertions.assertEquals(PK_POST, posts.getBody().get(INDEX).getPkPost()); 156 | Assertions.assertEquals(TITLE, posts.getBody().get(INDEX).getTitle()); 157 | Assertions.assertEquals(POST_CONTENT, posts.getBody().get(INDEX).getPostContent()); 158 | } 159 | 160 | @Test 161 | void whenFindByIdThenReturnAnPost() 162 | { 163 | Mockito.when(postService.findById(any())).thenReturn(post); 164 | 165 | ResponseEntity post = postController.findById(PK_POST); 166 | 167 | Assertions.assertNotNull(post); 168 | Assertions.assertEquals(HttpStatus.OK, post.getStatusCode()); 169 | Assertions.assertEquals(PK_POST, post.getBody().getPkPost()); 170 | Assertions.assertEquals(TITLE, post.getBody().getTitle()); 171 | Assertions.assertEquals(POST_CONTENT, post.getBody().getPostContent()); 172 | Assertions.assertEquals(author, post.getBody().getAuthor()); 173 | Assertions.assertEquals(Author.class, post.getBody().getAuthor().getClass()); 174 | Assertions.assertEquals(Post.class, post.getBody().getClass()); 175 | } 176 | 177 | 178 | @Test 179 | void whenDeleteByIdOccursSuccessfully() 180 | { 181 | doNothing().when(postService).deleteById(anyLong()); 182 | 183 | ResponseEntity response = postController.deleteById(PK_POST); 184 | 185 | assertNotNull(response); 186 | assertEquals(ResponseEntity.class, response.getClass()); 187 | assertEquals(HttpStatus.NO_CONTENT, response.getStatusCode()); 188 | verify(postService, times(1)).deleteById(anyLong()); 189 | 190 | } 191 | 192 | } -------------------------------------------------------------------------------- /backend/imperblog-backend/src/test/java/com/aureocarmelino/imperblogbackend/api/controller/TokenControllerTest.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.api.controller; 2 | 3 | import static org.mockito.Mockito.mock; 4 | import javax.servlet.http.HttpServletRequest; 5 | import javax.servlet.http.HttpServletResponse; 6 | import org.junit.jupiter.api.BeforeEach; 7 | import org.junit.jupiter.api.Test; 8 | import org.mockito.InjectMocks; 9 | import org.mockito.MockitoAnnotations; 10 | import org.springframework.boot.test.context.SpringBootTest; 11 | 12 | @SpringBootTest 13 | class TokenControllerTest 14 | { 15 | @InjectMocks 16 | private TokenController tokenController; 17 | 18 | private HttpServletRequest mockRequest; 19 | private HttpServletResponse mockResponse; 20 | 21 | @BeforeEach 22 | void init() throws Exception 23 | { 24 | MockitoAnnotations.openMocks(this); 25 | startInstances(); 26 | } 27 | 28 | public void startInstances() 29 | { 30 | mockRequest = mock(HttpServletRequest.class); 31 | 32 | mockResponse = mock(HttpServletResponse.class); 33 | } 34 | 35 | 36 | @Test 37 | void whenRevokeThenReturnStatusCode204() 38 | { 39 | tokenController.revoke(mockRequest, mockResponse); 40 | } 41 | } -------------------------------------------------------------------------------- /backend/imperblog-backend/src/test/java/com/aureocarmelino/imperblogbackend/api/repository/AuthorRepositoryTest.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.api.repository; 2 | 3 | 4 | 5 | import static org.assertj.core.api.Assertions.assertThat; 6 | import static org.junit.Assert.assertEquals; 7 | import static org.junit.Assert.assertNotNull; 8 | import static org.junit.Assert.assertThrows; 9 | 10 | import java.util.List; 11 | import java.util.Optional; 12 | 13 | import javax.validation.ConstraintViolationException; 14 | 15 | import org.junit.jupiter.api.BeforeEach; 16 | import org.junit.jupiter.api.Test; 17 | import org.junit.jupiter.api.extension.ExtendWith; 18 | import org.mockito.MockitoAnnotations; 19 | import org.springframework.beans.factory.annotation.Autowired; 20 | import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; 21 | import org.springframework.test.context.junit.jupiter.SpringExtension; 22 | 23 | import com.aureocarmelino.imperblogbackend.model.entity.Author; 24 | import com.aureocarmelino.imperblogbackend.model.entity.Gender; 25 | 26 | 27 | @ExtendWith(SpringExtension.class) 28 | @DataJpaTest 29 | class AuthorRepositoryTest 30 | { 31 | @Autowired 32 | private AuthorRepository authorRepository; 33 | 34 | private static final String EMAIL = "michael@hotmail.com"; 35 | private static final String PASSWORD = "michaelpassword"; 36 | private static final String USERNAME = "michael"; 37 | private static final String USERNAME_UPDATE = "michael.update"; 38 | private static final Gender GENDER = Gender.M; 39 | 40 | 41 | private static final String EMAIL2 = "michele@hotmail.com"; 42 | private static final String PASSWORD2 = "michelepassword"; 43 | private static final String USERNAME2 = "michele"; 44 | private static final Gender GENDER2 = Gender.F; 45 | 46 | private Author authorMichael; 47 | private Author authorMichele; 48 | 49 | @BeforeEach 50 | void init() throws Exception 51 | { 52 | MockitoAnnotations.openMocks(this); 53 | startInstances(); 54 | } 55 | 56 | public void startInstances() 57 | { 58 | 59 | authorMichael = Author.builder() 60 | .email(EMAIL) 61 | .username(USERNAME) 62 | .password(PASSWORD) 63 | .gender(GENDER) 64 | .build(); 65 | 66 | authorMichele = Author.builder() 67 | .email(EMAIL2) 68 | .username(USERNAME2) 69 | .password(PASSWORD2) 70 | .gender(GENDER2) 71 | .build(); 72 | } 73 | 74 | 75 | @Test 76 | void whenCreateThenPersistData() 77 | { 78 | authorRepository.save(authorMichael); 79 | 80 | assertNotNull(authorMichael.getPkAuthor()); 81 | 82 | assertEquals(EMAIL, authorMichael.getEmail()); 83 | assertEquals(PASSWORD, authorMichael.getPassword()); 84 | assertEquals(USERNAME, authorMichael.getUsername()); 85 | assertEquals(GENDER, authorMichael.getGender()); 86 | } 87 | 88 | @Test 89 | public void whenDeleteThenRemoveData() 90 | { 91 | authorRepository.save(authorMichael); 92 | 93 | authorRepository.delete(authorMichael); 94 | 95 | assertThat(authorRepository.findById(authorMichael.getPkAuthor())).isEmpty(); 96 | } 97 | 98 | @Test 99 | public void whenUpdateThenChangeAndPersistData() 100 | { 101 | authorRepository.save(authorMichael); 102 | 103 | authorMichael = authorRepository.findById(authorMichael.getPkAuthor()).orElse(null); 104 | authorMichael.setUsername(USERNAME_UPDATE); 105 | 106 | authorRepository.save(authorMichael); 107 | 108 | Author checkEditedAuthor = authorRepository.findById(authorMichael.getPkAuthor()).orElse(null); 109 | 110 | assertThat(checkEditedAuthor).isNotNull(); 111 | assertThat(checkEditedAuthor.getUsername()).isEqualTo(USERNAME_UPDATE); 112 | assertThat(checkEditedAuthor.getEmail()).isEqualTo(EMAIL); 113 | } 114 | 115 | @Test 116 | public void whenFindAllThenReturnAnList() 117 | { 118 | this.authorRepository.save(authorMichael); 119 | this.authorRepository.save(authorMichele); 120 | 121 | List authorList = authorRepository.findAll(); 122 | 123 | assertThat(authorList.size()).isEqualTo(2); 124 | } 125 | 126 | 127 | @Test 128 | public void whenFindByEmailThenReturnData() 129 | { 130 | authorRepository.save(authorMichael); 131 | 132 | Optional authorOptional = authorRepository.findByEmail(authorMichael.getEmail()); 133 | 134 | assertThat(authorOptional).isNotNull(); 135 | assertThat(authorOptional.get()).isNotNull(); 136 | assertThat(authorOptional.get().getUsername()).isEqualTo(USERNAME); 137 | assertThat(authorOptional.get().getEmail()).isEqualTo(EMAIL); 138 | } 139 | 140 | @Test 141 | public void whenFindByEmailThenReturnException() 142 | { 143 | authorMichael.setEmail("invalidemail@@@.com"); 144 | 145 | Exception exception = assertThrows(ConstraintViolationException.class, ()-> authorRepository.save(authorMichael)); 146 | 147 | assertEquals(ConstraintViolationException.class, exception.getClass()); 148 | } 149 | 150 | @Test 151 | public void whenFindByUsernameThenReturnData() 152 | { 153 | authorRepository.save(authorMichael); 154 | 155 | Optional authorOptional = authorRepository.findByUsername(authorMichael.getUsername()); 156 | 157 | assertThat(authorOptional).isNotNull(); 158 | assertThat(authorOptional.get()).isNotNull(); 159 | assertThat(authorOptional.get().getUsername()).isEqualTo(USERNAME); 160 | assertThat(authorOptional.get().getEmail()).isEqualTo(EMAIL); 161 | } 162 | 163 | @Test 164 | public void whenFindByUsernameThenReturnException() 165 | { 166 | Author author = Author.builder() 167 | .email(EMAIL) 168 | .password(PASSWORD) 169 | .gender(GENDER) 170 | .build(); 171 | 172 | Exception exception = assertThrows(ConstraintViolationException.class, ()-> authorRepository.save(author)); 173 | 174 | assertEquals(ConstraintViolationException.class, exception.getClass()); 175 | } 176 | 177 | @Test 178 | public void whenFindTotalAuthorsByGenderThenReturnAnList() 179 | { 180 | authorRepository.save(authorMichael); 181 | authorRepository.save(authorMichele); 182 | 183 | List authorList = authorRepository.findTotalAuthorsByGender(); 184 | 185 | assertThat(authorList.size()).isEqualTo(2); 186 | } 187 | 188 | } -------------------------------------------------------------------------------- /backend/imperblog-backend/src/test/java/com/aureocarmelino/imperblogbackend/api/repository/PostRepositoryTest.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.api.repository; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | import static org.junit.Assert.assertEquals; 5 | import static org.junit.Assert.assertNotNull; 6 | 7 | import java.util.List; 8 | 9 | import org.junit.jupiter.api.BeforeEach; 10 | import org.junit.jupiter.api.Test; 11 | import org.junit.jupiter.api.extension.ExtendWith; 12 | import org.mockito.MockitoAnnotations; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; 15 | import org.springframework.test.context.junit.jupiter.SpringExtension; 16 | 17 | import com.aureocarmelino.imperblogbackend.model.entity.Author; 18 | import com.aureocarmelino.imperblogbackend.model.entity.Gender; 19 | import com.aureocarmelino.imperblogbackend.model.entity.Post; 20 | 21 | 22 | @ExtendWith(SpringExtension.class) 23 | @DataJpaTest 24 | class PostRepositoryTest 25 | { 26 | @Autowired 27 | private PostRepository postRepository; 28 | 29 | @Autowired 30 | private AuthorRepository authorRepository; 31 | 32 | private static final String TITLE = "My new post"; 33 | private static final String TITLE_UPDATE = "My edited post"; 34 | private static final String POST_CONTENT = "This is my new post content"; 35 | 36 | private static final String EMAIL = "michael@hotmail.com"; 37 | private static final String PASSWORD = "michaelpassword"; 38 | private static final String USERNAME = "michael"; 39 | private static final Gender GENDER = Gender.M; 40 | 41 | private Author authorMichael; 42 | private Post post; 43 | 44 | @BeforeEach 45 | void init() throws Exception 46 | { 47 | MockitoAnnotations.openMocks(this); 48 | startInstances(); 49 | } 50 | 51 | public void startInstances() 52 | { 53 | authorMichael = Author.builder() 54 | .email(EMAIL) 55 | .username(USERNAME) 56 | .password(PASSWORD) 57 | .gender(GENDER) 58 | .build(); 59 | 60 | post = Post.builder() 61 | .title(TITLE) 62 | .postContent(POST_CONTENT) 63 | .author(authorMichael) 64 | .build(); 65 | } 66 | 67 | 68 | @Test 69 | void whenCreateThenPersistData() 70 | { 71 | authorRepository.save(authorMichael); 72 | postRepository.save(post); 73 | 74 | assertNotNull(post.getPkPost()); 75 | assertEquals(TITLE, post.getTitle()); 76 | assertEquals(POST_CONTENT, post.getPostContent()); 77 | assertEquals(authorMichael, post.getAuthor()); 78 | } 79 | 80 | @Test 81 | public void whenDeleteThenRemoveData() 82 | { 83 | authorRepository.save(authorMichael); 84 | postRepository.save(post); 85 | 86 | postRepository.delete(post); 87 | 88 | assertThat(postRepository.findById(post.getPkPost())).isEmpty(); 89 | } 90 | 91 | @Test 92 | public void whenUpdateThenChangeAndPersistData() 93 | { 94 | authorRepository.save(authorMichael); 95 | postRepository.save(post); 96 | 97 | post = postRepository.findById(post.getPkPost()).orElse(null); 98 | post.setTitle(TITLE_UPDATE); 99 | 100 | postRepository.save(post); 101 | 102 | Post checkEditedPost = postRepository.findById(post.getPkPost()).orElse(null); 103 | 104 | assertThat(checkEditedPost).isNotNull(); 105 | assertThat(checkEditedPost.getTitle()).isEqualTo(TITLE_UPDATE); 106 | assertThat(checkEditedPost.getPostContent()).isEqualTo(POST_CONTENT); 107 | } 108 | 109 | @Test 110 | public void whenFindAllThenReturnAnList() 111 | { 112 | authorRepository.save(authorMichael); 113 | this.postRepository.save(post); 114 | 115 | List postList = postRepository.findAll(); 116 | 117 | assertThat(postList.size()).isEqualTo(1); 118 | } 119 | 120 | @Test 121 | public void whenFindAllAuthorPostsThenReturnAnList() 122 | { 123 | authorRepository.save(authorMichael); 124 | this.postRepository.save(post); 125 | 126 | List postList = postRepository.findAllAuthorPosts(authorMichael); 127 | 128 | assertThat(postList.size()).isEqualTo(1); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /backend/imperblog-backend/src/test/java/com/aureocarmelino/imperblogbackend/api/service/AuthenticationServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.api.service; 2 | 3 | import static com.aureocarmelino.imperblogbackend.util.constant.ConstantAuthentication.ERROR_EMAIL_UNAVAILABLE; 4 | import static com.aureocarmelino.imperblogbackend.util.constant.ConstantAuthentication.ERROR_USER_UNAVAILABLE; 5 | import static com.aureocarmelino.imperblogbackend.util.constant.ConstantAuthentication.ERROR_INVALID_GENDER; 6 | import static org.mockito.ArgumentMatchers.any; 7 | import java.util.Optional; 8 | import org.junit.jupiter.api.Assertions; 9 | import org.junit.jupiter.api.BeforeEach; 10 | import org.junit.jupiter.api.Test; 11 | import org.mockito.InjectMocks; 12 | import org.mockito.Mock; 13 | import org.mockito.Mockito; 14 | import org.mockito.MockitoAnnotations; 15 | import org.modelmapper.ModelMapper; 16 | import org.springframework.boot.test.context.SpringBootTest; 17 | import org.springframework.security.crypto.password.PasswordEncoder; 18 | import com.aureocarmelino.imperblogbackend.api.repository.AuthorRepository; 19 | import com.aureocarmelino.imperblogbackend.model.dto.SignUpDto.SignUpDto; 20 | import com.aureocarmelino.imperblogbackend.model.entity.Author; 21 | import com.aureocarmelino.imperblogbackend.model.entity.Gender; 22 | import com.aureocarmelino.imperblogbackend.model.exception.BusinessException; 23 | 24 | @SpringBootTest 25 | class AuthenticationServiceTest 26 | { 27 | @Mock 28 | private ModelMapper modelMapper; 29 | 30 | @Mock 31 | private AuthorRepository authorRepository; 32 | 33 | @Mock 34 | private PasswordEncoder passwordEncoder; 35 | 36 | @InjectMocks 37 | private AuthenticationService authenticationService; 38 | 39 | private static final Long PK_AUTHOR = 1L; 40 | private static final String EMAIL = "aureo.roberto@hotmail.com"; 41 | private static final String PASSWORD = "mypassword"; 42 | private static final String USERNAME = "aureo.roberto"; 43 | private static final Gender GENDER = Gender.M; 44 | 45 | private SignUpDto signUpDto; 46 | private Author author; 47 | 48 | @BeforeEach 49 | void init() throws Exception 50 | { 51 | MockitoAnnotations.openMocks(this); 52 | startInstances(); 53 | } 54 | 55 | public void startInstances() 56 | { 57 | 58 | author = Author.builder() 59 | .pkAuthor(PK_AUTHOR) 60 | .email(EMAIL) 61 | .username(USERNAME) 62 | .password(PASSWORD) 63 | .gender(GENDER) 64 | .build(); 65 | 66 | signUpDto = SignUpDto.builder() 67 | .email(EMAIL) 68 | .password(PASSWORD) 69 | .username(USERNAME) 70 | .gender(GENDER.getDescription()) 71 | .build(); 72 | } 73 | 74 | 75 | @Test 76 | void whenSignupThenReturnAnAuthor() 77 | { 78 | Mockito.when(authorRepository.findByEmail(any())).thenReturn( Optional.empty()); 79 | Mockito.when(authorRepository.findByUsername(any())).thenReturn( Optional.empty()); 80 | Mockito.when(passwordEncoder.encode(any())).thenReturn(PASSWORD); 81 | Mockito.when(modelMapper.map(any(), any())).thenReturn(author); 82 | Mockito.when(authorRepository.save(any())).thenReturn(author); 83 | 84 | Author newAuthor = authenticationService.signup(signUpDto); 85 | 86 | Assertions.assertNotNull(newAuthor); 87 | Assertions.assertEquals(PK_AUTHOR, newAuthor.getPkAuthor()); 88 | Assertions.assertEquals(EMAIL, newAuthor.getEmail()); 89 | Assertions.assertEquals(PASSWORD, newAuthor.getPassword()); 90 | Assertions.assertEquals(USERNAME, newAuthor.getUsername()); 91 | Assertions.assertEquals(GENDER, newAuthor.getGender()); 92 | Assertions.assertEquals(author, newAuthor); 93 | Assertions.assertEquals(Author.class, newAuthor.getClass()); 94 | } 95 | 96 | @Test 97 | void whenSignupThenReturnAnBusinessExceptionError_Invalid_Gender() 98 | { 99 | try 100 | { 101 | signUpDto.setGender("INVALID_GENDER_TEST"); 102 | authenticationService.signup(signUpDto); 103 | } 104 | catch (Exception e) 105 | { 106 | Assertions.assertEquals(BusinessException.class, e.getClass()); 107 | Assertions.assertEquals(ERROR_INVALID_GENDER, e.getMessage()); 108 | } 109 | } 110 | 111 | @Test 112 | void whenSignupThenReturnAnBusinessExceptionError_Email_Unavailable() 113 | { 114 | Mockito.when(authorRepository.findByEmail(any())).thenThrow(new BusinessException(ERROR_EMAIL_UNAVAILABLE)); 115 | Mockito.when(authorRepository.findByUsername(any())).thenReturn(Optional.empty()); 116 | Mockito.when(passwordEncoder.encode(any())).thenReturn(PASSWORD); 117 | Mockito.when(modelMapper.map(any(), any())).thenReturn(author); 118 | Mockito.when(authorRepository.save(any())).thenReturn(author); 119 | 120 | try 121 | { 122 | authenticationService.signup(signUpDto); 123 | } 124 | catch (Exception e) 125 | { 126 | Assertions.assertEquals(BusinessException.class, e.getClass()); 127 | Assertions.assertEquals(ERROR_EMAIL_UNAVAILABLE, e.getMessage()); 128 | } 129 | } 130 | 131 | @Test 132 | void whenSignupThenReturnAnBusinessExceptionError_User_Unavailable() 133 | { 134 | Mockito.when(authorRepository.findByEmail(any())).thenReturn(Optional.empty()); 135 | Mockito.when(authorRepository.findByUsername(any())).thenThrow(new BusinessException(ERROR_USER_UNAVAILABLE)); 136 | Mockito.when(passwordEncoder.encode(any())).thenReturn(PASSWORD); 137 | Mockito.when(modelMapper.map(any(), any())).thenReturn(author); 138 | Mockito.when(authorRepository.save(any())).thenReturn(author); 139 | 140 | try 141 | { 142 | authenticationService.signup(signUpDto); 143 | } 144 | catch (Exception e) 145 | { 146 | Assertions.assertEquals(BusinessException.class, e.getClass()); 147 | Assertions.assertEquals(ERROR_USER_UNAVAILABLE, e.getMessage()); 148 | } 149 | } 150 | } -------------------------------------------------------------------------------- /backend/imperblog-backend/src/test/java/com/aureocarmelino/imperblogbackend/api/service/ChartServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.api.service; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | import org.junit.jupiter.api.Assertions; 6 | import org.junit.jupiter.api.BeforeEach; 7 | import org.junit.jupiter.api.Test; 8 | import org.mockito.InjectMocks; 9 | import org.mockito.Mock; 10 | import org.mockito.Mockito; 11 | import org.mockito.MockitoAnnotations; 12 | import org.springframework.boot.test.context.SpringBootTest; 13 | import com.aureocarmelino.imperblogbackend.api.repository.AuthorRepository; 14 | import com.aureocarmelino.imperblogbackend.model.dto.chartDto.ChartDto; 15 | import com.aureocarmelino.imperblogbackend.model.entity.Gender; 16 | 17 | @SpringBootTest 18 | class ChartServiceTest 19 | { 20 | 21 | @Mock 22 | private AuthorRepository authorRepository; 23 | 24 | @InjectMocks 25 | private ChartService chartService; 26 | 27 | private static final Long TOTAL_MALE = 6L; 28 | private static final String MALE = Gender.M.getDescription(); 29 | 30 | private static final Long TOTAL_FEMALE = 7L; 31 | private static final String FEMALE = Gender.F.getDescription(); 32 | 33 | private ChartDto chartDtoMale; 34 | 35 | private ChartDto chartDtoFemale; 36 | 37 | private List objectsChartData; 38 | 39 | @BeforeEach 40 | void init() throws Exception 41 | { 42 | MockitoAnnotations.openMocks(this); 43 | startInstances(); 44 | } 45 | 46 | public void startInstances() 47 | { 48 | 49 | chartDtoMale = ChartDto.builder() 50 | .gender(MALE) 51 | .total(TOTAL_MALE) 52 | .build(); 53 | 54 | chartDtoFemale = ChartDto.builder() 55 | .gender(FEMALE) 56 | .total(TOTAL_FEMALE) 57 | .build(); 58 | 59 | objectsChartData = Arrays.asList( 60 | new Object[]{chartDtoMale.getGender(), chartDtoMale.getTotal()}, 61 | new Object[]{chartDtoFemale.getGender(), chartDtoFemale.getTotal()}); 62 | } 63 | 64 | 65 | @Test 66 | void whenFindTotalAuthorsByGenderThenReturnChartData() 67 | { 68 | Mockito.when(authorRepository.findTotalAuthorsByGender()).thenReturn(objectsChartData); 69 | 70 | List listChartDto = chartService.findTotalAuthorsByGender(); 71 | 72 | Assertions.assertNotNull(listChartDto); 73 | Assertions.assertEquals(MALE, listChartDto.get(0).getGender()); 74 | Assertions.assertEquals(TOTAL_MALE, listChartDto.get(0).getTotal()); 75 | Assertions.assertEquals(FEMALE, listChartDto.get(1).getGender()); 76 | Assertions.assertEquals(TOTAL_FEMALE, listChartDto.get(1).getTotal()); 77 | } 78 | 79 | } -------------------------------------------------------------------------------- /backend/imperblog-backend/src/test/java/com/aureocarmelino/imperblogbackend/api/service/PostServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.aureocarmelino.imperblogbackend.api.service; 2 | 3 | import static org.mockito.ArgumentMatchers.*; 4 | import static org.mockito.Mockito.doNothing; 5 | import static org.mockito.Mockito.times; 6 | import static org.mockito.Mockito.verify; 7 | import java.util.List; 8 | import java.util.Optional; 9 | import org.junit.jupiter.api.Assertions; 10 | import org.junit.jupiter.api.BeforeEach; 11 | import org.junit.jupiter.api.Test; 12 | import org.mockito.InjectMocks; 13 | import org.mockito.Mock; 14 | import org.mockito.Mockito; 15 | import org.mockito.MockitoAnnotations; 16 | import org.modelmapper.ModelMapper; 17 | import org.springframework.boot.test.context.SpringBootTest; 18 | import com.aureocarmelino.imperblogbackend.api.repository.PostRepository; 19 | import com.aureocarmelino.imperblogbackend.model.dto.postDto.CreatePostDto; 20 | import com.aureocarmelino.imperblogbackend.model.entity.Author; 21 | import com.aureocarmelino.imperblogbackend.model.entity.Gender; 22 | import com.aureocarmelino.imperblogbackend.model.entity.Post; 23 | import com.aureocarmelino.imperblogbackend.model.exception.BusinessException; 24 | import com.aureocarmelino.imperblogbackend.model.exception.ObjectNotFoundException; 25 | import com.aureocarmelino.imperblogbackend.security.AuthorDetailsServiceImpl; 26 | import static com.aureocarmelino.imperblogbackend.util.constant.ConstantPost.ERROR_POST_DOESNT_BELONG_LOGGED_USER; 27 | import static com.aureocarmelino.imperblogbackend.util.constant.ConstantPost.ERROR_POST_NOT_FOUND; 28 | 29 | @SpringBootTest 30 | class PostServiceTest 31 | { 32 | @InjectMocks 33 | private PostService postService; 34 | 35 | @Mock 36 | private ModelMapper modelMapper; 37 | 38 | @Mock 39 | private PostRepository postRepository; 40 | 41 | @Mock 42 | private AuthorDetailsServiceImpl authorDetailsServiceImpl; 43 | 44 | private static final int INDEX = 0; 45 | 46 | private static final Long PK_POST = 1L; 47 | private static final String TITLE = "My new post"; 48 | private static final String POST_CONTENT = "This is my new post content"; 49 | 50 | private static final Long PK_AUTHOR = 1L; 51 | private static final String EMAIL = "aureo.roberto@hotmail.com"; 52 | private static final String PASSWORD = "mypassword"; 53 | private static final String USERNAME = "aureo.roberto"; 54 | private static final Gender GENDER = Gender.M; 55 | 56 | private Post post; 57 | private Author author; 58 | private CreatePostDto createPostDto; 59 | private Optional optionalPost; 60 | 61 | @BeforeEach 62 | void init() throws Exception 63 | { 64 | MockitoAnnotations.openMocks(this); 65 | startInstances(); 66 | } 67 | 68 | public void startInstances() 69 | { 70 | 71 | author = Author.builder() 72 | .pkAuthor(PK_AUTHOR) 73 | .email(EMAIL) 74 | .username(USERNAME) 75 | .password(PASSWORD) 76 | .gender(GENDER) 77 | .build(); 78 | 79 | post = Post.builder() 80 | .pkPost(PK_POST) 81 | .title(TITLE) 82 | .postContent(POST_CONTENT) 83 | .author(author) 84 | .build(); 85 | 86 | 87 | createPostDto = CreatePostDto.builder() 88 | .pkPost(PK_POST) 89 | .title(TITLE) 90 | .postContent(POST_CONTENT) 91 | .build(); 92 | 93 | optionalPost = Optional.of(Post.builder() 94 | .pkPost(PK_POST) 95 | .title(TITLE) 96 | .postContent(POST_CONTENT) 97 | .author(author) 98 | .build()); 99 | } 100 | 101 | 102 | @Test 103 | void whenCreateThenReturnAnPost() 104 | { 105 | 106 | Mockito.when(postRepository.save(any())).thenReturn(post); 107 | Mockito.when(modelMapper.map(any(), any())).thenReturn(post); 108 | Mockito.when(authorDetailsServiceImpl.logged()).thenReturn(author); 109 | 110 | Post newPost = postService.create(createPostDto); 111 | 112 | Assertions.assertNotNull(newPost); 113 | Assertions.assertEquals(PK_POST, newPost.getPkPost()); 114 | Assertions.assertEquals(TITLE, newPost.getTitle()); 115 | Assertions.assertEquals(POST_CONTENT, newPost.getPostContent()); 116 | Assertions.assertEquals(author, newPost.getAuthor()); 117 | Assertions.assertEquals(Author.class, newPost.getAuthor().getClass()); 118 | Assertions.assertEquals(Post.class, newPost.getClass()); 119 | } 120 | 121 | @Test 122 | void whenFindAllThenReturnAnListOfPosts() 123 | { 124 | Mockito.when(postRepository.findAll()).thenReturn(List.of(post)); 125 | 126 | List posts = postService.findAll(); 127 | 128 | Assertions.assertNotNull(posts); 129 | Assertions.assertEquals(PK_POST, posts.get(INDEX).getPkPost()); 130 | Assertions.assertEquals(TITLE, posts.get(INDEX).getTitle()); 131 | Assertions.assertEquals(POST_CONTENT, posts.get(INDEX).getPostContent()); 132 | Assertions.assertEquals(author, posts.get(INDEX).getAuthor()); 133 | Assertions.assertEquals(Author.class, posts.get(INDEX).getAuthor().getClass()); 134 | Assertions.assertEquals(Post.class, posts.get(INDEX).getClass()); 135 | } 136 | 137 | @Test 138 | void whenFindAllAuthorPostsThenReturnAnListOfAuthorPosts() 139 | { 140 | Mockito.when(postRepository.findAllAuthorPosts(any())).thenReturn(List.of(post)); 141 | 142 | List posts = postService.findAllAuthorPosts(); 143 | 144 | Assertions.assertNotNull(posts); 145 | Assertions.assertEquals(PK_POST, posts.get(INDEX).getPkPost()); 146 | Assertions.assertEquals(TITLE, posts.get(INDEX).getTitle()); 147 | Assertions.assertEquals(POST_CONTENT, posts.get(INDEX).getPostContent()); 148 | Assertions.assertEquals(author, posts.get(INDEX).getAuthor()); 149 | Assertions.assertEquals(Author.class, posts.get(INDEX).getAuthor().getClass()); 150 | Assertions.assertEquals(Post.class, posts.get(INDEX).getClass()); 151 | } 152 | 153 | @Test 154 | void whenFindByIdThenReturnAnPost() 155 | { 156 | Mockito.when(postRepository.findById(any())).thenReturn(optionalPost); 157 | 158 | Post posts = postService.findById(PK_POST); 159 | 160 | Assertions.assertNotNull(posts); 161 | Assertions.assertEquals(PK_POST, posts.getPkPost()); 162 | Assertions.assertEquals(TITLE, posts.getTitle()); 163 | Assertions.assertEquals(POST_CONTENT, posts.getPostContent()); 164 | Assertions.assertEquals(author, posts.getAuthor()); 165 | Assertions.assertEquals(Author.class, posts.getAuthor().getClass()); 166 | Assertions.assertEquals(Post.class, posts.getClass()); 167 | } 168 | 169 | 170 | @Test 171 | void whenFindByIdThenReturnAnObjectNotFoundException() 172 | { 173 | Mockito.when(postRepository.findById(any())).thenThrow(new ObjectNotFoundException(ERROR_POST_NOT_FOUND)); 174 | 175 | try 176 | { 177 | Post posts = postService.findById(PK_POST); 178 | Assertions.assertNotNull(posts); 179 | Assertions.assertEquals(PK_POST, posts.getPkPost()); 180 | Assertions.assertEquals(TITLE, posts.getTitle()); 181 | Assertions.assertEquals(POST_CONTENT, posts.getPostContent()); 182 | Assertions.assertEquals(author, posts.getAuthor()); 183 | Assertions.assertEquals(Author.class, posts.getAuthor().getClass()); 184 | Assertions.assertEquals(Post.class, posts.getClass()); 185 | } 186 | catch (Exception e) 187 | { 188 | Assertions.assertEquals(ObjectNotFoundException.class, e.getClass()); 189 | Assertions.assertEquals(ERROR_POST_NOT_FOUND, e.getMessage()); 190 | } 191 | } 192 | 193 | @Test 194 | void whenEditThenReturnAnEditedPost() 195 | { 196 | Mockito.when(postRepository.findById(any())).thenReturn(optionalPost); 197 | Mockito.when(postRepository.save(any())).thenReturn(post); 198 | Mockito.when(authorDetailsServiceImpl.logged()).thenReturn(author); 199 | 200 | Post editedPost = postService.edit(createPostDto); 201 | 202 | Assertions.assertNotNull(editedPost); 203 | Assertions.assertEquals(PK_POST, editedPost.getPkPost()); 204 | Assertions.assertEquals(TITLE, editedPost.getTitle()); 205 | Assertions.assertEquals(POST_CONTENT, editedPost.getPostContent()); 206 | Assertions.assertEquals(author, editedPost.getAuthor()); 207 | Assertions.assertEquals(Author.class, editedPost.getAuthor().getClass()); 208 | Assertions.assertEquals(Post.class, editedPost.getClass()); 209 | } 210 | 211 | @Test 212 | void whenEditThenReturnAnObjectNotFoundException() 213 | { 214 | Mockito.when(postRepository.findById(any())).thenReturn(optionalPost); 215 | 216 | try 217 | { 218 | postService.findById(PK_POST); 219 | } 220 | catch (Exception e) 221 | { 222 | Assertions.assertEquals(ObjectNotFoundException.class, e.getClass()); 223 | Assertions.assertEquals(ERROR_POST_NOT_FOUND, e.getMessage()); 224 | } 225 | } 226 | 227 | @Test 228 | void whenDeleteByIdOccursSuccessfully() 229 | { 230 | Mockito.when(authorDetailsServiceImpl.logged()).thenReturn(author); 231 | Mockito.when(postRepository.findById(any())).thenReturn(optionalPost); 232 | 233 | doNothing().when(postRepository).deleteById(anyLong()); 234 | 235 | postService.deleteById(PK_POST); 236 | 237 | verify(postRepository, times(1)).deleteById(anyLong()); 238 | } 239 | 240 | @Test 241 | void whenDeleteByIdThenReturnAnBusinessException() 242 | { 243 | Mockito.when(postRepository.findById(anyLong())).thenThrow(new BusinessException(ERROR_POST_DOESNT_BELONG_LOGGED_USER)); 244 | 245 | try 246 | { 247 | postService.deleteById(PK_POST); 248 | } 249 | catch (Exception ex) 250 | { 251 | Assertions.assertEquals(BusinessException.class, ex.getClass()); 252 | Assertions.assertEquals(ERROR_POST_DOESNT_BELONG_LOGGED_USER, ex.getMessage()); 253 | } 254 | } 255 | } -------------------------------------------------------------------------------- /backend/imperblog-backend/system.properties: -------------------------------------------------------------------------------- 1 | java.runtime.version=11 -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/.browserslistrc: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # For the full list of supported browsers by the Angular framework, please see: 6 | # https://angular.io/guide/browser-support 7 | 8 | # You can see what browsers were selected by your queries by running: 9 | # npx browserslist 10 | 11 | last 1 Chrome version 12 | last 1 Firefox version 13 | last 2 Edge major versions 14 | last 2 Safari major versions 15 | last 2 iOS major versions 16 | Firefox ESR 17 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://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 | [*.ts] 12 | quote_type = single 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | # Only exists if Bazel was run 8 | /bazel-out 9 | 10 | # dependencies 11 | /node_modules 12 | 13 | # profiling files 14 | chrome-profiler-events*.json 15 | 16 | # IDEs and editors 17 | /.idea 18 | .project 19 | .classpath 20 | .c9/ 21 | *.launch 22 | .settings/ 23 | *.sublime-workspace 24 | 25 | # IDE - VSCode 26 | .vscode/* 27 | !.vscode/settings.json 28 | !.vscode/tasks.json 29 | !.vscode/launch.json 30 | !.vscode/extensions.json 31 | .history/* 32 | 33 | # misc 34 | /.angular/cache 35 | /.sass-cache 36 | /connect.lock 37 | /coverage 38 | /libpeerconnection.log 39 | npm-debug.log 40 | yarn-error.log 41 | testem.log 42 | /typings 43 | 44 | # System Files 45 | .DS_Store 46 | Thumbs.db 47 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/README.md: -------------------------------------------------------------------------------- 1 | # ImperblogFrontend 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 13.0.2. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities. 24 | 25 | ## Further help 26 | 27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page. 28 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "imperblog-frontend": { 7 | "projectType": "application", 8 | "schematics": { 9 | "@schematics/angular:application": { 10 | "strict": true 11 | } 12 | }, 13 | "root": "", 14 | "sourceRoot": "src", 15 | "prefix": "app", 16 | "architect": { 17 | "build": { 18 | "builder": "@angular-devkit/build-angular:browser", 19 | "options": { 20 | "outputPath": "dist/imperblog-frontend", 21 | "index": "src/index.html", 22 | "main": "src/main.ts", 23 | "polyfills": "src/polyfills.ts", 24 | "tsConfig": "tsconfig.app.json", 25 | "assets": [ 26 | "src/favicon.ico", 27 | "src/assets" 28 | ], 29 | "styles": [ 30 | "src/styles.css", 31 | "node_modules/primeicons/primeicons.css", 32 | "node_modules/primeng/resources/themes/lara-light-blue/theme.css", 33 | "node_modules/primeng/resources/primeng.min.css", 34 | "node_modules/primeflex/primeflex.css", 35 | "node_modules/ngx-toastr/toastr.css", 36 | "node_modules/quill/dist/quill.core.css", 37 | "node_modules/quill/dist/quill.snow.css" 38 | ], 39 | "scripts": ["node_modules/quill/dist/quill.js", "node_modules/chart.js/dist/chart.js"] 40 | }, 41 | "configurations": { 42 | "production": { 43 | "budgets": [ 44 | { 45 | "type": "initial", 46 | "maximumWarning": "14mb", 47 | "maximumError": "17mb" 48 | }, 49 | { 50 | "type": "anyComponentStyle", 51 | "maximumWarning": "4kb", 52 | "maximumError": "8kb" 53 | } 54 | ], 55 | "fileReplacements": [ 56 | { 57 | "replace": "src/environments/environment.ts", 58 | "with": "src/environments/environment.prod.ts" 59 | } 60 | ], 61 | "outputHashing": "all" 62 | }, 63 | "development": { 64 | "buildOptimizer": false, 65 | "optimization": false, 66 | "vendorChunk": true, 67 | "extractLicenses": false, 68 | "sourceMap": true, 69 | "namedChunks": true 70 | } 71 | }, 72 | "defaultConfiguration": "production" 73 | }, 74 | "serve": { 75 | "builder": "@angular-devkit/build-angular:dev-server", 76 | "configurations": { 77 | "production": { 78 | "browserTarget": "imperblog-frontend:build:production" 79 | }, 80 | "development": { 81 | "browserTarget": "imperblog-frontend:build:development" 82 | } 83 | }, 84 | "defaultConfiguration": "development" 85 | }, 86 | "extract-i18n": { 87 | "builder": "@angular-devkit/build-angular:extract-i18n", 88 | "options": { 89 | "browserTarget": "imperblog-frontend:build" 90 | } 91 | }, 92 | "test": { 93 | "builder": "@angular-devkit/build-angular:karma", 94 | "options": { 95 | "main": "src/test.ts", 96 | "polyfills": "src/polyfills.ts", 97 | "tsConfig": "tsconfig.spec.json", 98 | "karmaConfig": "karma.conf.js", 99 | "assets": [ 100 | "src/favicon.ico", 101 | "src/assets" 102 | ], 103 | "styles": [ 104 | "src/styles.css" 105 | ], 106 | "scripts": [] 107 | } 108 | } 109 | } 110 | } 111 | }, 112 | 113 | "cli": { 114 | "analytics": false 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/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'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | jasmine: { 17 | // you can add configuration options for Jasmine here 18 | // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html 19 | // for example, you can disable the random execution with `random: false` 20 | // or set a specific seed with `seed: 4321` 21 | }, 22 | clearContext: false // leave Jasmine Spec Runner output visible in browser 23 | }, 24 | jasmineHtmlReporter: { 25 | suppressAll: true // removes the duplicated traces 26 | }, 27 | coverageReporter: { 28 | dir: require('path').join(__dirname, './coverage/imperblog-frontend'), 29 | subdir: '.', 30 | reporters: [ 31 | { type: 'html' }, 32 | { type: 'text-summary' } 33 | ] 34 | }, 35 | reporters: ['progress', 'kjhtml'], 36 | port: 9876, 37 | colors: true, 38 | logLevel: config.LOG_INFO, 39 | autoWatch: true, 40 | browsers: ['Chrome'], 41 | singleRun: false, 42 | restartOnFileChange: true 43 | }); 44 | }; 45 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "imperblog-frontend", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "watch": "ng build --watch --configuration development", 9 | "test": "ng test" 10 | }, 11 | "private": true, 12 | "dependencies": { 13 | "@angular/animations": "~13.0.0", 14 | "@angular/cdk": "^13.3.3", 15 | "@angular/common": "~13.0.0", 16 | "@angular/compiler": "~13.0.0", 17 | "@angular/core": "~13.0.0", 18 | "@angular/forms": "~13.0.0", 19 | "@angular/platform-browser": "~13.0.0", 20 | "@angular/platform-browser-dynamic": "~13.0.0", 21 | "@angular/router": "~13.0.0", 22 | "@auth0/angular-jwt": "^5.0.2", 23 | "bootstrap": "^5.1.3", 24 | "chart.js": "^3.8.0", 25 | "ngx-toastr": "^14.2.4", 26 | "primeflex": "^3.1.3", 27 | "primeicons": "^5.0.0", 28 | "primeng": "^13.3.3", 29 | "quill": "^1.3.7", 30 | "rxjs": "~7.4.0", 31 | "tslib": "^2.3.0", 32 | "zone.js": "~0.11.4" 33 | }, 34 | "devDependencies": { 35 | "@angular-devkit/build-angular": "^13.3.3", 36 | "@angular/cli": "^14.1.2", 37 | "@angular/compiler-cli": "~13.0.0", 38 | "@types/jasmine": "~3.10.0", 39 | "@types/node": "^12.11.1", 40 | "jasmine-core": "~3.10.0", 41 | "karma": "~6.3.0", 42 | "karma-chrome-launcher": "~3.1.0", 43 | "karma-coverage": "~2.0.3", 44 | "karma-jasmine": "~4.0.0", 45 | "karma-jasmine-html-reporter": "~1.7.0", 46 | "typescript": "~4.4.3" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | import { HomePageComponent } from './components/home-page/home-page.component'; 4 | import { LoginComponent } from './components/login/login.component'; 5 | import { PageNotFoundComponent } from './components/page-not-found/page-not-found.component'; 6 | import { ProfileComponent } from './components/profile/profile.component'; 7 | import { RegisterComponent } from './components/register/register.component'; 8 | import { ImperblogauthGuard } from './guard-routes/imperblogauth.guard'; 9 | 10 | const routes: Routes = 11 | [ 12 | { 13 | path: 'login', 14 | component: LoginComponent 15 | }, 16 | { 17 | path: 'register', 18 | component: RegisterComponent, 19 | }, 20 | { path: '', 21 | redirectTo: 'home', 22 | pathMatch: 'full' 23 | }, 24 | { 25 | path: 'home', 26 | component: HomePageComponent, 27 | canActivate: [ImperblogauthGuard], 28 | }, 29 | { 30 | path: 'profile', 31 | component: ProfileComponent, 32 | canActivate: [ImperblogauthGuard], 33 | }, 34 | { path: 'page-not-found', 35 | component: PageNotFoundComponent 36 | }, 37 | { 38 | path: '**', 39 | redirectTo: 'page-not-found' } 40 | ]; 41 | 42 | @NgModule({ 43 | imports: [RouterModule.forRoot(routes)], 44 | exports: [RouterModule] 45 | }) 46 | export class AppRoutingModule { } 47 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/app/app.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aureocarmelino/fullstack-blog-angular-and-spring/994e46b1dc948d7265f505d679b44c9264c5139e/frontend-angular/imperblog-frontend/src/app/app.component.css -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import {MegaMenuItem,MenuItem} from 'primeng/api'; 3 | 4 | @Component({ 5 | selector: 'app-root', 6 | templateUrl: './app.component.html', 7 | styleUrls: ['./app.component.css'] 8 | }) 9 | export class AppComponent 10 | { 11 | 12 | } 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { BrowserModule } from '@angular/platform-browser'; 3 | 4 | import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; 5 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 6 | import { AppRoutingModule } from './app-routing.module'; 7 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 8 | import { ToastrModule } from 'ngx-toastr'; 9 | import { AppComponent } from './app.component'; 10 | import { HeaderComponent } from './components/header/header.component'; 11 | import { LoginComponent } from './components/login/login.component'; 12 | import { RegisterComponent } from './components/register/register.component'; 13 | import { RouterModule } from '@angular/router'; 14 | import { JwtHelperService, JwtModule } from '@auth0/angular-jwt'; 15 | import {CardModule} from 'primeng/card'; 16 | import { HomePageComponent } from './components/home-page/home-page.component'; 17 | import {ButtonModule} from 'primeng/button'; 18 | import {ImageModule} from 'primeng/image'; 19 | import {DialogModule} from 'primeng/dialog'; 20 | import {DividerModule} from 'primeng/divider'; 21 | import {ScrollTopModule} from 'primeng/scrolltop'; 22 | import {ScrollPanelModule} from 'primeng/scrollpanel'; 23 | import {AvatarModule} from 'primeng/avatar'; 24 | import {SpeedDialModule} from 'primeng/speeddial'; 25 | import { ChipModule } from 'primeng/chip'; 26 | import { ProfileComponent } from './components/profile/profile.component'; 27 | import { HeaderProfileComponent } from './components/header-profile/header-profile.component'; 28 | import {EditorModule} from 'primeng/editor'; 29 | import {InputTextModule} from 'primeng/inputtext'; 30 | import {TooltipModule} from 'primeng/tooltip'; 31 | import { ImperBlogHttpInterceptor } from './interceptor/imper-blog-http-interceptor'; 32 | import { PageNotFoundComponent } from './components/page-not-found/page-not-found.component'; 33 | import { MessageService, ConfirmationService } from 'primeng/api'; 34 | import { AuthService } from './services/auth.service'; 35 | import {ChartModule} from 'primeng/chart'; 36 | import {RadioButtonModule} from 'primeng/radiobutton'; 37 | import { environment } from 'src/environments/environment'; 38 | 39 | 40 | 41 | 42 | export function tokenGetter(): string 43 | { 44 | return localStorage.getItem('token')!; 45 | } 46 | 47 | 48 | @NgModule({ 49 | declarations: 50 | [ 51 | AppComponent, 52 | HeaderComponent, 53 | LoginComponent, 54 | RegisterComponent, 55 | HomePageComponent, 56 | ProfileComponent, 57 | HeaderProfileComponent, 58 | PageNotFoundComponent 59 | ], 60 | imports: 61 | [ 62 | BrowserModule, 63 | BrowserAnimationsModule, 64 | AppRoutingModule, 65 | HttpClientModule, 66 | ToastrModule.forRoot(), 67 | FormsModule, 68 | ReactiveFormsModule, 69 | RouterModule, 70 | CardModule, 71 | ButtonModule, 72 | ImageModule, 73 | DialogModule, 74 | DividerModule, 75 | ScrollTopModule, 76 | ScrollPanelModule, 77 | AvatarModule, 78 | SpeedDialModule, 79 | ChipModule, 80 | RadioButtonModule, 81 | EditorModule, 82 | InputTextModule, 83 | ChartModule, 84 | TooltipModule, 85 | JwtModule.forRoot({ 86 | config: 87 | { 88 | tokenGetter, 89 | allowedDomains: environment.tokenAllowedDomains, 90 | disallowedRoutes: environment.disallowedRoutes 91 | } 92 | }) 93 | ], 94 | providers: 95 | [ 96 | AuthService, 97 | MessageService, 98 | ConfirmationService, 99 | 100 | JwtHelperService, 101 | { 102 | provide: HTTP_INTERCEPTORS, 103 | useClass: ImperBlogHttpInterceptor, 104 | multi: true, 105 | } 106 | ], 107 | bootstrap: [AppComponent] 108 | }) 109 | export class AppModule { } 110 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/app/components/header-profile/header-profile.component.css: -------------------------------------------------------------------------------- 1 | :host ::ng-deep .p-chip.custom-chip 2 | { 3 | background: #16a085; 4 | color: white; 5 | } 6 | 7 | :host ::ng-deep .p-chip.custom-chip:hover 8 | { 9 | background: #1abc9c; 10 | font-size: large; 11 | cursor: pointer; 12 | 13 | } 14 | 15 | :host ::ng-deep .p-avatar 16 | { 17 | border: 3px solid #16a085; 18 | } 19 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/app/components/header-profile/header-profile.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 | 7 |
8 |
9 | 10 |
11 |
12 | 13 |
14 |
15 | 16 | 17 | 18 |
19 |
20 | 21 |
22 |
23 | 24 |
25 | 26 |
27 | 28 |

{{authService.jwtPayload?.logged.username}}

29 | 30 |
31 |
32 | 33 |
34 | 35 |
36 | 37 | 38 | 49 | 50 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/app/components/header-profile/header-profile.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Router } from '@angular/router'; 3 | import { ErrorHandlerService } from 'src/app/services/error-handler.service'; 4 | import { AuthService } from 'src/app/services/auth.service'; 5 | import { LogoutService } from 'src/app/services/logout.service'; 6 | 7 | @Component({ 8 | selector: 'app-header-profile', 9 | templateUrl: './header-profile.component.html', 10 | styleUrls: ['./header-profile.component.css'] 11 | }) 12 | export class HeaderProfileComponent implements OnInit { 13 | 14 | constructor(public authService : AuthService, private logoutService: LogoutService, 15 | private router: Router, private errorHandler: ErrorHandlerService,) { } 16 | 17 | ngOnInit(): void {} 18 | 19 | logout() 20 | { 21 | this.logoutService.logout().then(() => {this.router.navigate(['/login']);}) 22 | .catch(erro => this.errorHandler.handle(erro)); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/app/components/header/header.component.css: -------------------------------------------------------------------------------- 1 | :host ::ng-deep .p-chip.custom-chip 2 | { 3 | background: #16a085; 4 | color: white; 5 | } 6 | 7 | :host ::ng-deep .p-chip.custom-chip:hover 8 | { 9 | background: #1abc9c; 10 | font-size: large; 11 | cursor: pointer; 12 | } 13 | 14 | :host ::ng-deep .p-avatar 15 | { 16 | border: 3px solid #16a085; 17 | } 18 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/app/components/header/header.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 | 7 |
8 |
9 | 10 |
11 | 12 |
13 | 14 |

Welcome {{authService.jwtPayload?.logged.username}}

15 | 16 |
17 |
18 | 19 |
20 |
21 | 22 | 23 | 24 |
25 |
26 | 27 |
28 |
29 | 30 | 31 | 32 |
33 |
34 | 35 |
36 | 37 | 39 | 40 | 41 | 42 | 43 | 44 | 49 | 50 | 51 | 52 | 53 | 54 |
55 | 56 | 58 |

Are you sure you want to leave ?

59 | 60 | 65 | 66 | 67 |
68 | 69 |
70 | 71 |
72 | 73 | 78 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/app/components/header/header.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Router } from '@angular/router'; 3 | import { ErrorHandlerService } from 'src/app/services/error-handler.service'; 4 | import { AuthService } from 'src/app/services/auth.service'; 5 | import { LogoutService } from 'src/app/services/logout.service'; 6 | import { ListPostService } from 'src/app/services/list-post.service'; 7 | 8 | @Component({ 9 | selector: 'app-header', 10 | templateUrl: './header.component.html', 11 | styleUrls: ['./header.component.css'] 12 | }) 13 | export class HeaderComponent implements OnInit 14 | { 15 | displayModalLogout!: boolean; 16 | displayModalChart!: boolean; 17 | pieChartData: any; 18 | 19 | 20 | constructor(public authService : AuthService, private logoutService: LogoutService, 21 | private router: Router, private errorHandler: ErrorHandlerService, private listPostService: ListPostService) { } 22 | 23 | ngOnInit(): void 24 | { 25 | this.loadChart(); 26 | } 27 | 28 | 29 | logout() 30 | { 31 | this.logoutService.logout().then(() => {this.router.navigate(['/login']);}).catch(erro => this.errorHandler.handle(erro)); 32 | } 33 | 34 | showModalDialogLogout() 35 | { 36 | this.displayModalLogout = true; 37 | } 38 | 39 | showModalDialogChart() 40 | { 41 | this.displayModalChart = true; 42 | } 43 | 44 | loadChart() 45 | { 46 | this.listPostService.findTotalAuthorsByGender().subscribe( result =>{ 47 | 48 | this.pieChartData = { 49 | // ['M', 'F'], 50 | labels: result.map(result => result.gender), 51 | datasets: [ 52 | { 53 | data: result.map(result => result.total), 54 | backgroundColor: result.map(result => result.color), 55 | 56 | } 57 | ] 58 | }; 59 | 60 | }) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/app/components/home-page/home-page.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aureocarmelino/fullstack-blog-angular-and-spring/994e46b1dc948d7265f505d679b44c9264c5139e/frontend-angular/imperblog-frontend/src/app/components/home-page/home-page.component.css -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/app/components/home-page/home-page.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

4 | 5 |
6 | 7 |
8 | 9 |
10 |
11 |

{{post.title}}

12 | 13 | 14 | {{post.author.username}} 15 | 16 | 17 |
18 |

19 |
20 | 21 | 22 | 23 | 25 |
26 |

27 | 28 | 29 | 32 | 33 |
34 | 35 |
36 |
37 |
38 | 39 |
40 | Lista Vazia 41 |
42 |
43 | 44 |
45 | 46 |
47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/app/components/home-page/home-page.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { ToastrService } from 'ngx-toastr'; 3 | import { MenuItem } from 'primeng/api/menuitem'; 4 | import { Post } from 'src/app/models/Post'; 5 | import { ErrorHandlerService } from 'src/app/services/error-handler.service'; 6 | import { ListPostService } from 'src/app/services/list-post.service'; 7 | import { AuthService } from 'src/app/services/auth.service'; 8 | 9 | @Component({ 10 | selector: 'app-home-page', 11 | templateUrl: './home-page.component.html', 12 | styleUrls: ['./home-page.component.css'] 13 | }) 14 | export class HomePageComponent implements OnInit 15 | { 16 | displayMaximizable: boolean = false; 17 | listPost: Post[] = []; 18 | postUser = new Post(); 19 | totalPost = 0; 20 | 21 | constructor(private listPostService : ListPostService, private toastr: ToastrService, private auth: AuthService, 22 | private errorHandler: ErrorHandlerService) {} 23 | 24 | ngOnInit(): void 25 | { 26 | this.findAllPost(); 27 | } 28 | 29 | showMaximizableDialog() 30 | { 31 | this.displayMaximizable = true; 32 | } 33 | 34 | findAllPost(): void 35 | { 36 | this.listPostService.findAllPost().subscribe((result) => 37 | { 38 | this.listPost = result 39 | this.totalPost = this.listPost.length; 40 | }, 41 | err => 42 | { 43 | this.errorHandler.handle(err) 44 | this.toastr.error("Error processing posts. Please try again!", "ERROR", {progressBar : true}) 45 | }) 46 | 47 | 48 | } 49 | 50 | findByPostId(id : number): void 51 | { 52 | this.listPostService.findByPostId(id).subscribe( (result) => 53 | { 54 | this.showMaximizableDialog(); 55 | this.postUser = result; 56 | 57 | }, 58 | err => 59 | { 60 | this.errorHandler.handle(err); 61 | this.toastr.error("Error processing posts. Please try again!", "ERROR", {progressBar : true}) 62 | }); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/app/components/login/login.component.css: -------------------------------------------------------------------------------- 1 | 2 | * 3 | { 4 | margin: 0; 5 | padding: 0; 6 | box-sizing: border-box; 7 | font-family: 'Poppins', sans-serif; 8 | overflow: hidden; 9 | } 10 | 11 | section 12 | { 13 | position: relative; 14 | width: 100%; 15 | height: 100vh; 16 | display: flex; 17 | } 18 | 19 | 20 | section .imgBx 21 | { 22 | position: relative; 23 | width: 50%; 24 | height: 100%; 25 | } 26 | 27 | section .imgBx img 28 | { 29 | position: absolute; 30 | top: 0; 31 | left: 0; 32 | height: 100%; 33 | object-fit: cover; 34 | } 35 | 36 | section .contentBx 37 | { 38 | display: flex; 39 | justify-content: center; 40 | align-items: center; 41 | width: 60%; 42 | height: 100%; 43 | } 44 | 45 | section .contentBx .formBx 46 | { 47 | width: 50%; 48 | } 49 | 50 | section .contentBx .formBx h2 51 | { 52 | color: #607d8b; 53 | font-weight: 600; 54 | font-size: 1.5em; 55 | text-transform: uppercase; 56 | margin-bottom: 20px; 57 | border-bottom: 4px solid #ff4584; 58 | letter-spacing: 1px; 59 | } 60 | 61 | section .contentBx .formBx .inputBx 62 | { 63 | margin-bottom: 20px; 64 | } 65 | 66 | section .contentBx .formBx .inputBx span 67 | { 68 | font-size: 16px; 69 | margin-bottom: 5px; 70 | display: inline-block; 71 | color: #607db8; 72 | font-weight: 300; 73 | font-size: 16px; 74 | letter-spacing: 1px; 75 | } 76 | 77 | section .contentBx .formBx .inputBx input 78 | { 79 | width: 100%; 80 | padding: 10px 20px; 81 | outline: none; 82 | font-weight: 400; 83 | border: 1px solid #607d8b; 84 | background: transparent; 85 | border-radius: 30px; 86 | } 87 | 88 | section .contentBx .formBx .inputBx input[type="submit"] 89 | { 90 | background: #ff4584; 91 | color: #fff; 92 | outline: none; 93 | border: none; 94 | font-weight: 500; 95 | cursor: pointer; 96 | } 97 | 98 | section .contentBx .formBx .inputBx input[type="submit"]:hover 99 | { 100 | background: #f53677; 101 | } 102 | 103 | section .contentBx .formBx .inputBx p 104 | { 105 | color: #607d8b; 106 | } 107 | 108 | section .contentBx .formBx .inputBx p a 109 | { 110 | color: #ff4584; 111 | } 112 | 113 | section .contentBx .formBx h3 114 | { 115 | color: #607d8b; 116 | text-align: center; 117 | margin: 80px 0 10px; 118 | font-weight: 500; 119 | } 120 | 121 | 122 | section .contentBx .formBx .sci 123 | { 124 | display: flex; 125 | justify-content: center; 126 | align-items: center; 127 | } 128 | 129 | section .contentBx .formBx .sci li 130 | { 131 | list-style: none; 132 | width: 50px; 133 | height: 50px; 134 | display: flex; 135 | justify-content: center; 136 | align-items: center; 137 | background: #607d8b; 138 | border-radius: 50%; 139 | margin: 0 7px; 140 | cursor: pointer; 141 | } 142 | 143 | section .contentBx .formBx .sci li:hover 144 | { 145 | background: #ff4584; 146 | } 147 | 148 | section .contentBx .formBx .sci li img 149 | { 150 | transform: scale(0.5); 151 | filter: invert(1); 152 | } 153 | 154 | @media (max-width: 768px) 155 | { 156 | section .imgBx 157 | { 158 | position: absolute; 159 | top: 0; 160 | left: 0; 161 | width: 100%; 162 | height: 100%; 163 | } 164 | 165 | section .contentBx 166 | { 167 | display: flex; 168 | justify-content: center; 169 | align-items: center; 170 | width: 100%; 171 | height: 100%; 172 | z-index: 1; 173 | } 174 | 175 | section .contentBx .formBx 176 | { 177 | width: 100%; 178 | padding: 40px; 179 | background: #fff; 180 | margin: 50px; 181 | } 182 | 183 | section .contentBx .formBx h3 184 | { 185 | color: #607d8b; 186 | text-align: center; 187 | margin: 30px 0 10px; 188 | font-weight: 500; 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/app/components/login/login.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 | 7 |

LOGIN | WELCOME

8 | 9 |
10 | 11 |
12 | 13 | Email 14 | 15 | 16 |
17 | 18 |
19 | 20 | Password 21 | 22 | 23 |
24 | 25 |
26 | 27 |
28 | 29 |
30 |

Don't have an account ? Sign up

31 |
32 | 33 |
34 | 35 |
36 |
37 | 38 |
39 | 40 |
41 |
42 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/app/components/login/login.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { FormControl, FormGroup } from '@angular/forms'; 3 | import { Router } from '@angular/router'; 4 | import { ToastrService } from 'ngx-toastr'; 5 | import { LoginDto } from 'src/app/models/LoginDto'; 6 | import { ErrorHandlerService } from 'src/app/services/error-handler.service'; 7 | import { AuthService } from 'src/app/services/auth.service'; 8 | 9 | @Component({ 10 | selector: 'app-login', 11 | templateUrl: './login.component.html', 12 | styleUrls: ['./login.component.css'] 13 | }) 14 | export class LoginComponent implements OnInit 15 | { 16 | newLogin = new LoginDto(); 17 | 18 | constructor(public authService : AuthService, private toastr: ToastrService, private router: Router, 19 | private errorHandler: ErrorHandlerService){} 20 | 21 | ngOnInit(): void{} 22 | 23 | loginUser() : void 24 | { 25 | this.authService.login(this.newLogin).then((response) => 26 | { 27 | this.toastr.success('Authenticated ', 'Success'); 28 | this.router.navigateByUrl('/home'); 29 | }) 30 | .catch(err => 31 | { 32 | this.errorHandler.handle(err); 33 | 34 | this.toastr.error(err, "ERROR", 35 | { 36 | progressBar : true, 37 | 38 | }) 39 | }); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/app/components/page-not-found/page-not-found.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aureocarmelino/fullstack-blog-angular-and-spring/994e46b1dc948d7265f505d679b44c9264c5139e/frontend-angular/imperblog-frontend/src/app/components/page-not-found/page-not-found.component.css -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/app/components/page-not-found/page-not-found.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | Page Not Found 4 | 5 |
6 |
7 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/app/components/page-not-found/page-not-found.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-page-not-found', 5 | templateUrl: './page-not-found.component.html', 6 | styleUrls: ['./page-not-found.component.css'] 7 | }) 8 | export class PageNotFoundComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit(): void {} 13 | 14 | } 15 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/app/components/profile/profile.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aureocarmelino/fullstack-blog-angular-and-spring/994e46b1dc948d7265f505d679b44c9264c5139e/frontend-angular/imperblog-frontend/src/app/components/profile/profile.component.css -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/app/components/profile/profile.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |
6 | 7 |
Create a new post
8 |
9 |
10 |
Title
11 | 12 | 13 | 14 |

15 | 16 |
Content
17 | 18 | 19 |
20 |
21 | 22 | 23 |
24 | 25 | 26 | 27 |
28 | 29 |
My Latest Posts
30 | 31 |
32 | 33 |
34 | 35 |
36 |
37 |

{{post.title}}

38 | 39 | 40 | {{post.author.username}} 41 | 42 | 43 |
44 | 45 |

46 | 47 |
48 | 49 | 50 | 51 | 52 | 53 | 55 |
56 | 57 |
Title
58 | 59 | 60 | 61 |

62 | 63 |
Content
64 | 65 | 66 | 67 | 71 | 72 | 73 |
74 | 75 | 77 |

Are you sure you want to delete this post ( {{postUser.title}} ) ?

78 | 79 | 84 | 85 | 86 |
87 | 88 |
89 |
90 |
91 | 92 |
93 | Lista Vazia 94 |
95 |
96 | 97 |
98 | 99 | 100 | 101 |
102 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/app/components/profile/profile.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Router } from '@angular/router'; 3 | import { ToastrService } from 'ngx-toastr'; 4 | import { CreatePostDto } from 'src/app/models/CreatePostDto'; 5 | import { Post } from 'src/app/models/Post'; 6 | import { AddPostService } from 'src/app/services/add-post.service'; 7 | import { ErrorHandlerService } from 'src/app/services/error-handler.service'; 8 | import { ListPostService } from 'src/app/services/list-post.service'; 9 | 10 | @Component({ 11 | selector: 'app-profile', 12 | templateUrl: './profile.component.html', 13 | styleUrls: ['./profile.component.css'] 14 | }) 15 | export class ProfileComponent implements OnInit { 16 | 17 | displayMaximizable!: boolean; 18 | displayModalDeletePost!: boolean; 19 | newPost = new CreatePostDto(); 20 | postUser = new CreatePostDto(); 21 | 22 | listMyPosts: Post[] = []; 23 | 24 | totalTarefas = 0; 25 | 26 | 27 | constructor(public addPostService : AddPostService, private toastr: ToastrService, private router: Router, 28 | private listPostService : ListPostService, private errorHandler: ErrorHandlerService,) { } 29 | 30 | ngOnInit(): void 31 | { 32 | this.findAllMyPost(); 33 | } 34 | 35 | addNewPost(post : CreatePostDto): void 36 | { 37 | this.addPostService.addPost(post).subscribe(() => 38 | { 39 | this.newPost = new CreatePostDto(); 40 | this.postUser = new CreatePostDto(); 41 | this.toastr.success('Post saved successfully', 'Success !'); 42 | this.findAllMyPost(); 43 | 44 | }, 45 | err => 46 | { 47 | 48 | console.log(err) 49 | this.errorHandler.handle(err) 50 | 51 | if (err.status == 0 && err.statusText == "Unknown Error") 52 | { 53 | this.toastr.error("Server Failed or Offline", "ERRO 500", { 54 | progressBar : true 55 | }) 56 | } 57 | 58 | if ( err.error.fieldErrors ) 59 | { 60 | err.error.fieldErrors.forEach( (element:any) => 61 | { 62 | this.toastr.error(element.error) 63 | }); 64 | } 65 | else 66 | { 67 | this.toastr.error(err.error.message); 68 | } 69 | 70 | if (err.status == 400 && err.statusText == "OK") 71 | { 72 | this.toastr.error(err.error.title, "ERROR 400", { 73 | progressBar : true 74 | }) 75 | } 76 | 77 | } 78 | ) 79 | } 80 | 81 | editPost(post : CreatePostDto): void 82 | { 83 | this.addPostService.editPost(post).subscribe(() => 84 | { 85 | this.newPost = new CreatePostDto(); 86 | this.postUser = new CreatePostDto(); 87 | this.toastr.success('Post edited successfully', 'Success !'); 88 | this.findAllMyPost(); 89 | 90 | }, 91 | err => 92 | { 93 | 94 | console.log(err) 95 | this.errorHandler.handle(err) 96 | 97 | if (err.status == 0 && err.statusText == "Unknown Error") 98 | { 99 | this.toastr.error("Server Failed or Offline", "ERRO 500", { 100 | progressBar : true 101 | }) 102 | } 103 | 104 | if ( err.error.fieldErrors ) 105 | { 106 | err.error.fieldErrors.forEach( (element:any) => 107 | { 108 | this.toastr.error(element.error) 109 | }); 110 | } 111 | else 112 | { 113 | this.toastr.error(err.error.message); 114 | } 115 | 116 | if (err.status == 400 && err.statusText == "OK") 117 | { 118 | this.toastr.error(err.error.title, "ERROR 400", { 119 | progressBar : true 120 | }) 121 | } 122 | 123 | } 124 | ) 125 | } 126 | 127 | showMaximizableDialog() 128 | { 129 | this.displayMaximizable = true; 130 | } 131 | 132 | showModalDialogDeletePost() 133 | { 134 | this.displayModalDeletePost = true; 135 | } 136 | 137 | findAllMyPost(): void 138 | { 139 | this.listPostService.findAllMyPost().subscribe((result) => 140 | { 141 | this.listMyPosts = result 142 | this.totalTarefas = this.listMyPosts.length; 143 | }, 144 | err => 145 | { 146 | this.errorHandler.handle(err) 147 | 148 | this.toastr.error("Error processing the last posts. Please try again!", "ERROR", {progressBar : true}) 149 | 150 | }) 151 | } 152 | 153 | findByPostIdEdit(id : number): void 154 | { 155 | this.listPostService.findByPostId(id).subscribe( (result) => { 156 | 157 | this.postUser = result; 158 | this.showMaximizableDialog(); 159 | }, 160 | err => 161 | { 162 | this.errorHandler.handle(err) 163 | 164 | this.toastr.error("Error processing post. Please try again!", "ERROR", { 165 | progressBar : true 166 | }) 167 | } 168 | 169 | ); 170 | 171 | } 172 | 173 | findByPostIdDelete(id : number): void 174 | { 175 | this.listPostService.findByPostId(id).subscribe( (result) => { 176 | 177 | this.postUser = result; 178 | this.showModalDialogDeletePost(); 179 | }, 180 | err => 181 | { 182 | this.errorHandler.handle(err) 183 | 184 | this.toastr.error("Error processing post. Please try again!", "ERROR", { 185 | progressBar : true 186 | }) 187 | } 188 | 189 | ); 190 | 191 | } 192 | 193 | deleteById(id : number): void 194 | { 195 | this.listPostService.deleteById(id).subscribe( () => { 196 | 197 | this.toastr.success('Post deleted successfully', 'Success !'); 198 | this.findAllMyPost(); 199 | }, 200 | err => 201 | { 202 | this.errorHandler.handle(err) 203 | 204 | this.toastr.error("Error deleting post. Please try again!", "ERROR", { 205 | progressBar : true 206 | }) 207 | } 208 | 209 | ); 210 | 211 | } 212 | 213 | } 214 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/app/components/register/register.component.css: -------------------------------------------------------------------------------- 1 | 2 | * 3 | { 4 | margin: 0; 5 | padding: 0; 6 | box-sizing: border-box; 7 | font-family: 'Poppins', sans-serif; 8 | overflow: hidden; 9 | } 10 | 11 | section 12 | { 13 | position: relative; 14 | width: 100%; 15 | height: 100vh; 16 | display: flex; 17 | } 18 | 19 | 20 | section .imgBx 21 | { 22 | position: relative; 23 | width: 50%; 24 | height: 100%; 25 | } 26 | 27 | section .imgBx img 28 | { 29 | position: absolute; 30 | top: 0; 31 | left: 0; 32 | height: 100%; 33 | object-fit: cover; 34 | } 35 | 36 | 37 | section .contentBx 38 | { 39 | display: flex; 40 | justify-content: center; 41 | align-items: center; 42 | width: 60%; 43 | height: 100%; 44 | } 45 | 46 | section .contentBx .formBx 47 | { 48 | width: 50%; 49 | } 50 | 51 | section .contentBx .formBx h2 52 | { 53 | color: #607d8b; 54 | font-weight: 600; 55 | font-size: 1.5em; 56 | text-transform: uppercase; 57 | margin-bottom: 20px; 58 | border-bottom: 4px solid #ff4584; 59 | letter-spacing: 1px; 60 | } 61 | 62 | section .contentBx .formBx .inputBx 63 | { 64 | margin-bottom: 20px; 65 | } 66 | 67 | section .contentBx .formBx .inputBx span 68 | { 69 | font-size: 16px; 70 | margin-bottom: 5px; 71 | display: inline-block; 72 | color: #607db8; 73 | font-weight: 300; 74 | font-size: 16px; 75 | letter-spacing: 1px; 76 | } 77 | 78 | section .contentBx .formBx .inputBx input 79 | { 80 | width: 100%; 81 | padding: 10px 20px; 82 | outline: none; 83 | font-weight: 400; 84 | border: 1px solid #607d8b; 85 | background: transparent; 86 | border-radius: 30px; 87 | } 88 | 89 | section .contentBx .formBx .inputBx input[type="submit"] 90 | { 91 | background: #ff4584; 92 | color: #fff; 93 | outline: none; 94 | border: none; 95 | font-weight: 500; 96 | cursor: pointer; 97 | } 98 | 99 | section .contentBx .formBx .inputBx input[type="submit"]:hover 100 | { 101 | background: #f53677; 102 | } 103 | 104 | section .contentBx .formBx .inputBx p 105 | { 106 | color: #607d8b; 107 | } 108 | 109 | section .contentBx .formBx .inputBx p a 110 | { 111 | color: #ff4584; 112 | } 113 | 114 | section .contentBx .formBx h3 115 | { 116 | color: #607d8b; 117 | text-align: center; 118 | margin: 80px 0 10px; 119 | font-weight: 500; 120 | } 121 | 122 | 123 | section .contentBx .formBx .sci 124 | { 125 | display: flex; 126 | justify-content: center; 127 | align-items: center; 128 | } 129 | 130 | section .contentBx .formBx .sci li 131 | { 132 | list-style: none; 133 | width: 50px; 134 | height: 50px; 135 | display: flex; 136 | justify-content: center; 137 | align-items: center; 138 | background: #607d8b; 139 | border-radius: 50%; 140 | margin: 0 7px; 141 | cursor: pointer; 142 | } 143 | 144 | section .contentBx .formBx .sci li:hover 145 | { 146 | background: #ff4584; 147 | } 148 | 149 | section .contentBx .formBx .sci li img 150 | { 151 | transform: scale(0.5); 152 | filter: invert(1); 153 | } 154 | 155 | @media (max-width: 768px) 156 | { 157 | section .imgBx 158 | { 159 | position: absolute; 160 | top: 0; 161 | left: 0; 162 | width: 100%; 163 | height: 100%; 164 | } 165 | 166 | section .contentBx 167 | { 168 | display: flex; 169 | justify-content: center; 170 | align-items: center; 171 | width: 100%; 172 | height: 100%; 173 | z-index: 1; 174 | } 175 | 176 | section .contentBx .formBx 177 | { 178 | width: 100%; 179 | padding: 40px; 180 | background: #fff; 181 | margin: 50px; 182 | } 183 | 184 | section .contentBx .formBx h3 185 | { 186 | color: #607d8b; 187 | text-align: center; 188 | margin: 30px 0 10px; 189 | font-weight: 500; 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/app/components/register/register.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

Sign Up

5 | 6 |
7 | 8 |
9 | Email 10 | 11 |
12 | 13 |
14 | Username 15 | 16 |
17 | 18 |
19 | Password 20 | 21 |
22 | 23 |
24 | 25 |
26 | M 27 | 28 |
29 | 30 |
31 | F 32 | 33 |
34 | 35 |
36 | 37 | 38 |
39 | 40 |
41 | 42 |
43 |

Do you have an account ? Sign in

44 |
45 | 46 | 47 | 48 |
49 | 50 |
51 |
52 | 53 |
54 | 55 |
56 |
57 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/app/components/register/register.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Author } from 'src/app/models/Author'; 3 | import { SignupService } from 'src/app/services/signup.service'; 4 | import { ToastrService } from 'ngx-toastr'; 5 | import { Router } from '@angular/router'; 6 | import { FormBuilder, FormGroup } from '@angular/forms'; 7 | import { ErrorHandlerService } from 'src/app/services/error-handler.service'; 8 | 9 | @Component({ 10 | selector: 'app-register', 11 | templateUrl: './register.component.html', 12 | styleUrls: ['./register.component.css'] 13 | }) 14 | export class RegisterComponent implements OnInit 15 | { 16 | newAuthor = new Author(); 17 | gender!: string ; 18 | 19 | 20 | constructor( private rout: Router, private signupService : SignupService,private toastr: ToastrService, 21 | private errorHandler: ErrorHandlerService,){} 22 | 23 | ngOnInit(): void{} 24 | 25 | 26 | signup(): void 27 | { 28 | this.newAuthor.gender = this.gender; 29 | 30 | 31 | this.signupService.signup(this.newAuthor).subscribe(() => 32 | { 33 | this.toastr.success('Account created successfully', 'Created !'); 34 | this.rout.navigateByUrl('/login'); 35 | }, 36 | err => 37 | { 38 | this.errorHandler.handle(err); 39 | 40 | if (err.status == 0 && err.statusText == "Unknown Error") 41 | { 42 | this.toastr.error("Server Failed or Offline", "ERROR 500", { 43 | progressBar : true 44 | }) 45 | } 46 | else if (err.status == 400 && err.statusText == "OK") 47 | { 48 | this.toastr.error(err.error.title, "ERROR 400", { 49 | progressBar : true 50 | }) 51 | } 52 | else if (err.status == 401) 53 | { 54 | this.toastr.error(err.error, "ERROR 401", { 55 | progressBar : true 56 | }) 57 | } 58 | 59 | if ( err.error.fieldErrors) 60 | { 61 | err.error.fieldErrors.forEach( (element:any) => 62 | { 63 | this.toastr.error(element.error) 64 | }, 65 | () => { 66 | this.toastr.error(err.error.message); 67 | } 68 | ); 69 | } 70 | else 71 | { 72 | this.toastr.error(err.name); 73 | } 74 | 75 | } 76 | ) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/app/guard-routes/imperblogauth.guard.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree } from '@angular/router'; 3 | import { Observable } from 'rxjs'; 4 | import { AuthService } from '../services/auth.service'; 5 | 6 | @Injectable({ 7 | providedIn: 'root' 8 | }) 9 | export class ImperblogauthGuard implements CanActivate 10 | { 11 | constructor( 12 | private auth: AuthService, 13 | private router: Router 14 | ) {} 15 | 16 | canActivate( route: ActivatedRouteSnapshot, 17 | state: RouterStateSnapshot): Observable | Promise | boolean | UrlTree 18 | { 19 | 20 | if (this.auth.isAccessTokenInvalid()) 21 | { 22 | //console.log('Browsing with invalid access token. Getting new token...'); 23 | 24 | return this.auth.getNewAccessToken() 25 | .then(() => 26 | { 27 | if (this.auth.isAccessTokenInvalid()) 28 | { 29 | this.router.navigate(['/login']); 30 | return false; 31 | } 32 | 33 | return true; 34 | }); 35 | } 36 | return true; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/app/interceptor/imper-blog-http-interceptor.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http'; 3 | import { Observable, from } from 'rxjs'; 4 | import { mergeMap } from 'rxjs/operators'; 5 | import { AuthService } from '../services/auth.service'; 6 | 7 | export class NotAuthenticatedError {} 8 | 9 | @Injectable() 10 | export class ImperBlogHttpInterceptor implements HttpInterceptor 11 | { 12 | 13 | constructor(private auth: AuthService) {} 14 | 15 | intercept(req: HttpRequest, next: HttpHandler): Observable> 16 | { 17 | 18 | if(req.url.includes('/api/auth/signup')) 19 | { 20 | return next.handle(req); 21 | } 22 | 23 | if (!req.url.includes('/oauth/token') && this.auth.isAccessTokenInvalid()) 24 | { 25 | return from(this.auth.getNewAccessToken()) 26 | .pipe( 27 | mergeMap(() => { 28 | 29 | if (this.auth.isAccessTokenInvalid()) 30 | { 31 | throw new NotAuthenticatedError(); 32 | } 33 | 34 | alert(localStorage.getItem('token')) 35 | req = req.clone({ 36 | setHeaders: { 37 | Authorization: `Bearer ${localStorage.getItem('token')}` 38 | } 39 | }); 40 | return next.handle(req); 41 | }) 42 | ); 43 | } 44 | return next.handle(req); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/app/models/Author.ts: -------------------------------------------------------------------------------- 1 | 2 | export class Author 3 | { 4 | pkAuthor?: number; 5 | email?: string; 6 | password?: string; 7 | username?: string; 8 | gender?: string; 9 | } 10 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/app/models/ChartDto.ts: -------------------------------------------------------------------------------- 1 | export class ChartDto 2 | { 3 | gender?: String; 4 | total?: number; 5 | color?: String; 6 | } 7 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/app/models/CreatePostDto.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | export class CreatePostDto 4 | { 5 | pkPost?: number; 6 | title?: String; 7 | postContent?: String; 8 | } 9 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/app/models/LoginDto.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | export class LoginDto 4 | { 5 | email?: String; 6 | password?: String; 7 | } 8 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/app/models/Post.ts: -------------------------------------------------------------------------------- 1 | import { Author } from "./Author"; 2 | 3 | export class Post 4 | { 5 | pkPost!: number; 6 | author = new Author(); 7 | postContent?: String; 8 | title?: String; 9 | } 10 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/app/services/add-post.service.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient } from '@angular/common/http'; 2 | import { Injectable } from '@angular/core'; 3 | import { Observable } from 'rxjs'; 4 | import { environment } from 'src/environments/environment'; 5 | import { CreatePostDto } from '../models/CreatePostDto'; 6 | import { Post } from '../models/Post'; 7 | 8 | @Injectable({ 9 | providedIn: 'root' 10 | }) 11 | export class AddPostService 12 | { 13 | url = environment.baseUrl; 14 | 15 | constructor(private http: HttpClient){} 16 | 17 | addPost(newPost: CreatePostDto): Observable 18 | { 19 | return this.http.post(this.url + "/api/posts", newPost); 20 | } 21 | 22 | editPost(editPost: CreatePostDto): Observable 23 | { 24 | return this.http.put(this.url + "/api/posts", editPost); 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/app/services/auth.service.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; 2 | import { Injectable } from '@angular/core'; 3 | import { JwtHelperService } from '@auth0/angular-jwt'; 4 | import { Observable } from 'rxjs'; 5 | import { environment } from 'src/environments/environment'; 6 | import { LoginDto } from '../models/LoginDto'; 7 | 8 | @Injectable({ 9 | providedIn: 'root' 10 | }) 11 | export class AuthService 12 | { 13 | url = environment.baseUrl; 14 | jwtPayload: any; 15 | 16 | constructor(private http: HttpClient, private jwtHelper: JwtHelperService) 17 | { 18 | this.loadToken(); 19 | } 20 | 21 | login(newLogin: LoginDto): Promise 22 | { 23 | var header = new HttpHeaders(); 24 | header = header.append('Content-Type', 'application/x-www-form-urlencoded'); 25 | header = header.append('Authorization', 'Basic dGVzdGU6dGVzdGU='); 26 | 27 | var body = new HttpParams(); 28 | body = body.append('username', newLogin.email as string); 29 | body = body.append('password', newLogin.password as string); 30 | body = body.append('grant_type', 'password'); 31 | 32 | 33 | return this.http.post(this.url + "/oauth/token", body, {headers: header, withCredentials: true}).toPromise() 34 | .then((response:any) => 35 | { 36 | this.storeToken(response['access_token']); 37 | }) 38 | .catch(response => 39 | { 40 | if (response.status === 400) 41 | { 42 | if (response.error.error === 'invalid_grant') 43 | { 44 | return Promise.reject('Username or password is invalid!'); 45 | } 46 | } 47 | 48 | if (response.status === 0) 49 | { 50 | if (response.statusText === 'Unknown Error') 51 | { 52 | return Promise.reject('Unavailable server'); 53 | } 54 | } 55 | return Promise.reject(response); 56 | }); 57 | 58 | } 59 | 60 | getNewAccessToken(): Promise 61 | { 62 | var header = new HttpHeaders(); 63 | header = header.append('Content-Type', 'application/x-www-form-urlencoded'); 64 | header = header.append('Authorization', 'Basic dGVzdGU6dGVzdGU='); 65 | 66 | var body = new HttpParams(); 67 | body = body.append('grant_type', 'refresh_token'); 68 | 69 | return this.http.post(this.url + "/oauth/token", body,{ headers: header, withCredentials: true}).toPromise() 70 | .then((response:any) => 71 | { 72 | this.storeToken(response['access_token']); 73 | return Promise.resolve(); 74 | 75 | }).catch(response => 76 | { 77 | return Promise.resolve(); 78 | }); 79 | } 80 | 81 | isAccessTokenInvalid() 82 | { 83 | const token = localStorage.getItem('token'); 84 | return !token || this.jwtHelper.isTokenExpired(token); 85 | } 86 | 87 | public storeToken(token: string) 88 | { 89 | this.jwtPayload = this.jwtHelper.decodeToken(token); 90 | // console.log(JSON.stringify(this.jwtPayload)) 91 | localStorage.setItem('token', token); 92 | 93 | 94 | } 95 | 96 | public loadToken() 97 | { 98 | const token = localStorage.getItem('token'); 99 | 100 | if ( token ) 101 | { 102 | this.storeToken(token); 103 | } 104 | } 105 | 106 | clearAccessToken() 107 | { 108 | localStorage.removeItem('token'); 109 | this.jwtPayload = null; 110 | 111 | //console.log("DELETING TOKEN") 112 | } 113 | 114 | } 115 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/app/services/error-handler.service.ts: -------------------------------------------------------------------------------- 1 | import { HttpErrorResponse } from '@angular/common/http'; 2 | import { Injectable } from '@angular/core'; 3 | import { Router } from '@angular/router'; 4 | import { MessageService } from 'primeng/api'; 5 | import { NotAuthenticatedError } from '../interceptor/imper-blog-http-interceptor'; 6 | 7 | @Injectable({ 8 | providedIn: 'root' 9 | }) 10 | export class ErrorHandlerService 11 | { 12 | constructor(private messageService: MessageService,private router: Router) { } 13 | 14 | handle(errorResponse: any) 15 | { 16 | let msg: string; 17 | 18 | if (typeof errorResponse === 'string') 19 | { 20 | msg = errorResponse; 21 | } 22 | else if (errorResponse instanceof NotAuthenticatedError) 23 | { 24 | msg = 'Your session has expired!'; 25 | this.messageService.add({ severity:'error', detail: msg }); 26 | this.router.navigate(['/']); 27 | 28 | } 29 | else if (errorResponse instanceof HttpErrorResponse && errorResponse.status >= 400 && errorResponse.status <= 499) 30 | { 31 | msg = 'There was an error processing your request'; 32 | 33 | if (errorResponse.status === 403) 34 | { 35 | msg = 'You are not allowed to perform this action'; 36 | } 37 | 38 | try 39 | { 40 | msg = errorResponse.error[0].mensagemUsuario; 41 | } 42 | catch (e) { } 43 | 44 | //console.error('An error has occurred', errorResponse); 45 | } 46 | else 47 | { 48 | msg = 'Error processing remote service. Try again.'; 49 | //console.error('An error has occurred', errorResponse); 50 | } 51 | this.messageService.add({ severity:'error', detail: msg }); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/app/services/list-post.service.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient, HttpHeaders } from '@angular/common/http'; 2 | import { Injectable } from '@angular/core'; 3 | import { Observable } from 'rxjs'; 4 | import { environment } from 'src/environments/environment'; 5 | import { ChartDto } from '../models/ChartDto'; 6 | import { Post } from '../models/Post'; 7 | 8 | @Injectable({ 9 | providedIn: 'root' 10 | }) 11 | export class ListPostService { 12 | 13 | url = environment.baseUrl; 14 | 15 | constructor(private http: HttpClient){} 16 | 17 | findAllPost(): Observable 18 | { 19 | var header = new HttpHeaders(); 20 | header = header.append('Content-Type', 'application/json'); 21 | 22 | return this.http.get(this.url + "/api/posts", {headers: header}); 23 | } 24 | 25 | 26 | 27 | findAllMyPost(): Observable 28 | { 29 | var header = new HttpHeaders(); 30 | header = header.append('Content-Type', 'application/json'); 31 | 32 | return this.http.get(this.url + "/api/posts/user", {headers: header}); 33 | } 34 | 35 | findByPostId(id : number): Observable 36 | { 37 | var header = new HttpHeaders(); 38 | header = header.append('Content-Type', 'application/json'); 39 | 40 | return this.http.get(this.url + "/api/posts/"+id, {headers: header}); 41 | } 42 | 43 | findTotalAuthorsByGender(): Observable 44 | { 45 | var header = new HttpHeaders(); 46 | header = header.append('Content-Type', 'application/json'); 47 | 48 | return this.http.get(this.url + "/api/chart", {headers: header}); 49 | } 50 | 51 | deleteById(id: number): Observable 52 | { 53 | return this.http.delete(this.url + "/api/posts/"+id); 54 | } 55 | 56 | 57 | } 58 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/app/services/logout.service.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient } from '@angular/common/http'; 2 | import { Injectable } from '@angular/core'; 3 | import { environment } from 'src/environments/environment'; 4 | import { AuthService } from './auth.service'; 5 | 6 | @Injectable({ 7 | providedIn: 'root' 8 | }) 9 | export class LogoutService { 10 | 11 | tokensRevokeUrl = environment.baseUrl; 12 | 13 | constructor( private http: HttpClient, private auth: AuthService) { } 14 | 15 | async logout() 16 | { 17 | await this.http.delete(this.tokensRevokeUrl+"/tokens/revoke", { withCredentials: true }).toPromise(); 18 | this.auth.clearAccessToken(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/app/services/signup.service.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient } from '@angular/common/http'; 2 | import { Injectable } from '@angular/core'; 3 | import { Observable } from 'rxjs'; 4 | import { environment } from 'src/environments/environment'; 5 | import { Author } from '../models/Author'; 6 | 7 | @Injectable({ 8 | providedIn: 'root' 9 | }) 10 | export class SignupService 11 | { 12 | url = environment.baseUrl; 13 | 14 | constructor(private http: HttpClient){} 15 | 16 | signup(newAuthor: Author): Observable 17 | { 18 | return this.http.post(this.url + "/api/auth/signup", newAuthor); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aureocarmelino/fullstack-blog-angular-and-spring/994e46b1dc948d7265f505d679b44c9264c5139e/frontend-angular/imperblog-frontend/src/assets/.gitkeep -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/assets/img/crown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aureocarmelino/fullstack-blog-angular-and-spring/994e46b1dc948d7265f505d679b44c9264c5139e/frontend-angular/imperblog-frontend/src/assets/img/crown.png -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/assets/img/emptyList.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aureocarmelino/fullstack-blog-angular-and-spring/994e46b1dc948d7265f505d679b44c9264c5139e/frontend-angular/imperblog-frontend/src/assets/img/emptyList.png -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/assets/img/error-404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aureocarmelino/fullstack-blog-angular-and-spring/994e46b1dc948d7265f505d679b44c9264c5139e/frontend-angular/imperblog-frontend/src/assets/img/error-404.png -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/assets/img/login.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aureocarmelino/fullstack-blog-angular-and-spring/994e46b1dc948d7265f505d679b44c9264c5139e/frontend-angular/imperblog-frontend/src/assets/img/login.avif -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false, 7 | baseUrl: 'http://localhost:8080', 8 | tokenAllowedDomains: ["localhost:8080"], 9 | disallowedRoutes: [/\/oauth\/token/, /\/api\/auth\/signup/] 10 | }; 11 | 12 | /* 13 | * For easier debugging in development mode, you can import the following file 14 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 15 | * 16 | * This import should be commented out in production mode because it will have a negative impact 17 | * on performance if an error is thrown. 18 | */ 19 | // import 'zone.js/plugins/zone-error'; // Included with Angular CLI. 20 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aureocarmelino/fullstack-blog-angular-and-spring/994e46b1dc948d7265f505d679b44c9264c5139e/frontend-angular/imperblog-frontend/src/favicon.ico -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Imperblog 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/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.error(err)); 13 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/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 recent versions of Safari, Chrome (including 12 | * Opera), Edge on the desktop, and iOS and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** 22 | * By default, zone.js will patch all possible macroTask and DomEvents 23 | * user can disable parts of macroTask/DomEvents patch by setting following flags 24 | * because those flags need to be set before `zone.js` being loaded, and webpack 25 | * will put import in the top of bundle, so user need to create a separate file 26 | * in this directory (for example: zone-flags.ts), and put the following flags 27 | * into that file, and then add the following code before importing zone.js. 28 | * import './zone-flags'; 29 | * 30 | * The flags allowed in zone-flags.ts are listed here. 31 | * 32 | * The following flags will work for all browsers. 33 | * 34 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 35 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 36 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 37 | * 38 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 39 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 40 | * 41 | * (window as any).__Zone_enable_cross_context_check = true; 42 | * 43 | */ 44 | 45 | /*************************************************************************************************** 46 | * Zone JS is required by default for Angular itself. 47 | */ 48 | import 'zone.js'; // Included with Angular CLI. 49 | 50 | 51 | /*************************************************************************************************** 52 | * APPLICATION IMPORTS 53 | */ 54 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | 3 | @import "~bootstrap/dist/css/bootstrap.css"; 4 | 5 | /* RESET */ 6 | 7 | 8 | * 9 | { 10 | margin: 0; 11 | padding: 0; 12 | font-size: 100%; 13 | border: none; 14 | outline: none; 15 | font-weight: 300; 16 | box-sizing: border-box; 17 | } 18 | 19 | 20 | body 21 | { 22 | background-color: #f7f7f7; 23 | } 24 | 25 | a 26 | { 27 | text-decoration: none; 28 | } 29 | 30 | ul 31 | { 32 | list-style: none; 33 | } 34 | 35 | img 36 | { 37 | max-width: 100%; 38 | } 39 | 40 | .publicacoes, .container-fluid 41 | { 42 | width: 100%; 43 | } 44 | 45 | .publicacao 46 | { 47 | width: 90%; 48 | background-color: white; 49 | margin: 2em auto 2em auto; 50 | box-shadow: 0 5px 5px #16a085; 51 | word-wrap: break-word; 52 | } 53 | 54 | #buttonView, #buttonDeleted 55 | { 56 | padding: 10px; 57 | border-radius: 5px; 58 | } 59 | 60 | #buttonView:hover 61 | { 62 | background-color: rgb(240, 255, 247); 63 | } 64 | 65 | #buttonDeleted:hover 66 | { 67 | background-color: rgb(255, 240, 245); 68 | } 69 | 70 | .textPost 71 | { 72 | max-height: 10ch; 73 | overflow: hidden; 74 | text-overflow: ellipsis; 75 | white-space: nowrap; 76 | } 77 | 78 | 79 | 80 | 81 | /* ================================================== MOBILE FIRST ==================================================*/ 82 | 83 | 84 | /* SMALL DEVICES - SMARTPHONES */ 85 | 86 | @media screen and (min-width: 480px) 87 | { 88 | 89 | .publicacao 90 | { 91 | width: 80%; 92 | } 93 | 94 | } 95 | /* SMALL DEVICES - TABLETS*/ 96 | @media screen and (min-width: 700px) 97 | { 98 | .publicacao 99 | { 100 | width: 70%; 101 | } 102 | } 103 | /* MEDIUM DEVICES - TABLETS & DESKTOPS*/ 104 | @media screen and (min-width: 900px) 105 | { 106 | .publicacao 107 | { 108 | width: 60%; 109 | } 110 | } 111 | 112 | 113 | /* LARGE DEVICES - WIDE SCREENS*/ 114 | @media screen and (min-width: 1180px) 115 | { 116 | .publicacao 117 | { 118 | width: 50%; 119 | } 120 | } 121 | 122 | 123 | /* personalizar a barra em geral, aqui estou definindo 10px de largura para a barra vertical 124 | e 10px de altura para a barra horizontal */ 125 | ::-webkit-scrollbar 126 | { 127 | width: 10px; 128 | height: 3%; 129 | } 130 | ::-webkit-scrollbar-track 131 | { 132 | background:#000; 133 | } 134 | 135 | /* aqui é a alça da barra, que demonstra a altura que você está na página 136 | estou colocando uma cor azul clara nela*/ 137 | ::-webkit-scrollbar-thumb 138 | { 139 | background: linear-gradient(transparent, #16a085); 140 | border-radius: 10px; 141 | } 142 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/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/testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: { 11 | context(path: string, deep?: boolean, filter?: RegExp): { 12 | keys(): string[]; 13 | (id: string): T; 14 | }; 15 | }; 16 | 17 | // First, initialize the Angular testing environment. 18 | getTestBed().initTestEnvironment( 19 | BrowserDynamicTestingModule, 20 | platformBrowserDynamicTesting(), 21 | ); 22 | 23 | // Then we find all the tests. 24 | const context = require.context('./', true, /\.spec\.ts$/); 25 | // And load the modules. 26 | context.keys().map(context); 27 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/app", 6 | "types": [] 7 | }, 8 | "files": [ 9 | "src/main.ts", 10 | "src/polyfills.ts" 11 | ], 12 | "include": [ 13 | "src/**/*.d.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "baseUrl": "./", 6 | "outDir": "./dist/out-tsc", 7 | "forceConsistentCasingInFileNames": true, 8 | "strict": true, 9 | "noImplicitOverride": true, 10 | "noPropertyAccessFromIndexSignature": true, 11 | "noImplicitReturns": true, 12 | "noFallthroughCasesInSwitch": true, 13 | "sourceMap": true, 14 | "declaration": false, 15 | "downlevelIteration": true, 16 | "experimentalDecorators": true, 17 | "moduleResolution": "node", 18 | "importHelpers": true, 19 | "target": "es2017", 20 | "module": "es2020", 21 | "lib": [ 22 | "es2020", 23 | "dom" 24 | ] 25 | }, 26 | "angularCompilerOptions": { 27 | "enableI18nLegacyMessageIdFormat": false, 28 | "strictInjectionParameters": true, 29 | "strictInputAccessModifiers": true, 30 | "strictTemplates": true 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /frontend-angular/imperblog-frontend/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/spec", 6 | "types": [ 7 | "jasmine" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts", 12 | "src/polyfills.ts" 13 | ], 14 | "include": [ 15 | "src/**/*.spec.ts", 16 | "src/**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /myassets/blog_app.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aureocarmelino/fullstack-blog-angular-and-spring/994e46b1dc948d7265f505d679b44c9264c5139e/myassets/blog_app.gif -------------------------------------------------------------------------------- /myassets/create_database.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aureocarmelino/fullstack-blog-angular-and-spring/994e46b1dc948d7265f505d679b44c9264c5139e/myassets/create_database.PNG -------------------------------------------------------------------------------- /myassets/login.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aureocarmelino/fullstack-blog-angular-and-spring/994e46b1dc948d7265f505d679b44c9264c5139e/myassets/login.PNG -------------------------------------------------------------------------------- /myassets/print_postman_collection.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aureocarmelino/fullstack-blog-angular-and-spring/994e46b1dc948d7265f505d679b44c9264c5139e/myassets/print_postman_collection.PNG -------------------------------------------------------------------------------- /myassets/run_test.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aureocarmelino/fullstack-blog-angular-and-spring/994e46b1dc948d7265f505d679b44c9264c5139e/myassets/run_test.PNG -------------------------------------------------------------------------------- /myassets/run_test2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aureocarmelino/fullstack-blog-angular-and-spring/994e46b1dc948d7265f505d679b44c9264c5139e/myassets/run_test2.PNG -------------------------------------------------------------------------------- /myassets/unit_run_tests.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aureocarmelino/fullstack-blog-angular-and-spring/994e46b1dc948d7265f505d679b44c9264c5139e/myassets/unit_run_tests.gif -------------------------------------------------------------------------------- /myassets/unit_test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aureocarmelino/fullstack-blog-angular-and-spring/994e46b1dc948d7265f505d679b44c9264c5139e/myassets/unit_test.png -------------------------------------------------------------------------------- /myassets/unit_test2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aureocarmelino/fullstack-blog-angular-and-spring/994e46b1dc948d7265f505d679b44c9264c5139e/myassets/unit_test2.png --------------------------------------------------------------------------------