├── .gitignore ├── LICENSE ├── README.md ├── app ├── .gitignore ├── Dockerfile ├── LICENSE ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── hellokoding │ │ └── account │ │ ├── WebApplication.java │ │ ├── model │ │ ├── MailProperties.java │ │ ├── User.java │ │ ├── VerificationForm.java │ │ └── VerificationToken.java │ │ ├── repository │ │ ├── UserRepository.java │ │ └── VerificationTokenRepository.java │ │ ├── service │ │ ├── SendingMailService.java │ │ └── VerificationTokenService.java │ │ └── web │ │ └── AccountController.java │ └── resources │ ├── application.properties │ ├── static │ ├── css │ │ └── main.css │ └── js │ │ └── main.js │ └── templates │ ├── email-verification.ftl │ └── verification-form.ftl ├── docker-compose.yaml └── nginx └── conf.d └── app.conf /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | target 3 | *.iml 4 | out 5 | .DS_Store 6 | data 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Hello Koding 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [Email Verification Example with Spring Boot, MySQL, Docker Compose, Amazon SES](https://hellokoding.com/email-verification-example-with-spring-boot-mysql-docker-compose/) 2 | 3 | ## What you'll need 4 | - Docker CE 5 | - Amazon SES account 6 | - SMTP Credentials of your Amazon SES account 7 | - Verified From email and To email on SES console 8 | 9 | ## Stack 10 | - Docker 11 | - Spring Boot 12 | - Java Mail API 13 | - Spring Data JPA 14 | - MySQL 15 | - NGINX 16 | - Maven 17 | 18 | ## Run 19 | - Run command `docker-compose up` 20 | - Access to http://localhost/ 21 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | target 3 | *.iml 4 | out 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /app/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM maven:3.5-jdk-8 2 | -------------------------------------------------------------------------------- /app/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Hello Koding 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /app/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | com.hellokoding 5 | email-verification-springboot-mysql-dockercompose 6 | email-verification-springboot-mysql-dockercompose 7 | 8 | org.springframework.boot 9 | spring-boot-starter-parent 10 | 1.5.9.RELEASE 11 | 12 | 13 | 14 | 1.8 15 | 16 | 17 | 18 | 19 | org.springframework.boot 20 | spring-boot-starter-freemarker 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-data-jpa 25 | 26 | 27 | mysql 28 | mysql-connector-java 29 | 30 | 31 | org.hibernate 32 | hibernate-java8 33 | 34 | 35 | com.sun.mail 36 | javax.mail 37 | 1.6.0 38 | 39 | 40 | 41 | 42 | 43 | org.springframework.boot 44 | spring-boot-maven-plugin 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /app/src/main/java/com/hellokoding/account/WebApplication.java: -------------------------------------------------------------------------------- 1 | package com.hellokoding.account; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class WebApplication { 8 | public static void main(String[] args) throws Exception { 9 | SpringApplication.run(WebApplication.class, args); 10 | } 11 | } -------------------------------------------------------------------------------- /app/src/main/java/com/hellokoding/account/model/MailProperties.java: -------------------------------------------------------------------------------- 1 | package com.hellokoding.account.model; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | import org.springframework.stereotype.Component; 5 | 6 | @Component 7 | @ConfigurationProperties(prefix = "mail") 8 | public class MailProperties { 9 | public static class SMTP { 10 | String host; 11 | String port; 12 | String username; 13 | String password; 14 | 15 | public String getHost() { 16 | return host; 17 | } 18 | 19 | public void setHost(String host) { 20 | this.host = host; 21 | } 22 | 23 | public String getPort() { 24 | return port; 25 | } 26 | 27 | public void setPort(String port) { 28 | this.port = port; 29 | } 30 | 31 | public String getUsername() { 32 | return username; 33 | } 34 | 35 | public void setUsername(String username) { 36 | this.username = username; 37 | } 38 | 39 | public String getPassword() { 40 | return password; 41 | } 42 | 43 | public void setPassword(String password) { 44 | this.password = password; 45 | } 46 | } 47 | 48 | private SMTP smtp; 49 | private String from; 50 | private String fromName; 51 | private String verificationapi; 52 | 53 | public SMTP getSmtp() { 54 | return smtp; 55 | } 56 | 57 | public void setSmtp(SMTP smtp) { 58 | this.smtp = smtp; 59 | } 60 | 61 | public String getFrom() { 62 | return from; 63 | } 64 | 65 | public void setFrom(String from) { 66 | this.from = from; 67 | } 68 | 69 | public String getFromName() { 70 | return fromName; 71 | } 72 | 73 | public void setFromName(String fromName) { 74 | this.fromName = fromName; 75 | } 76 | 77 | public String getVerificationapi() { 78 | return verificationapi; 79 | } 80 | 81 | public void setVerificationapi(String verificationapi) { 82 | this.verificationapi = verificationapi; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /app/src/main/java/com/hellokoding/account/model/User.java: -------------------------------------------------------------------------------- 1 | package com.hellokoding.account.model; 2 | 3 | import javax.persistence.*; 4 | 5 | @Entity 6 | public class User { 7 | private Long id; 8 | private String email; 9 | private Boolean isActive; 10 | private VerificationToken verificationToken; 11 | 12 | @Id 13 | @GeneratedValue(strategy = GenerationType.AUTO) 14 | public Long getId() { 15 | return id; 16 | } 17 | 18 | public void setId(Long id) { 19 | this.id = id; 20 | } 21 | 22 | public String getEmail() { 23 | return email; 24 | } 25 | 26 | public void setEmail(String email) { 27 | this.email = email; 28 | } 29 | 30 | public Boolean getIsActive() { 31 | return isActive; 32 | } 33 | 34 | public void setIsActive(Boolean isActive) { 35 | this.isActive = isActive; 36 | } 37 | 38 | @OneToOne(mappedBy = "user", cascade = CascadeType.ALL) 39 | public VerificationToken getVerificationToken() { 40 | return verificationToken; 41 | } 42 | 43 | public void setVerificationToken(VerificationToken verificationToken) { 44 | this.verificationToken = verificationToken; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/java/com/hellokoding/account/model/VerificationForm.java: -------------------------------------------------------------------------------- 1 | package com.hellokoding.account.model; 2 | 3 | import org.hibernate.validator.constraints.Email; 4 | import org.hibernate.validator.constraints.NotEmpty; 5 | 6 | public class VerificationForm { 7 | @NotEmpty 8 | @Email 9 | private String email; 10 | 11 | public String getEmail() { 12 | return email; 13 | } 14 | 15 | public void setEmail(String email) { 16 | this.email = email; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/hellokoding/account/model/VerificationToken.java: -------------------------------------------------------------------------------- 1 | package com.hellokoding.account.model; 2 | 3 | import javax.persistence.*; 4 | import java.time.LocalDateTime; 5 | import java.util.UUID; 6 | 7 | @Entity 8 | public class VerificationToken { 9 | public static final String STATUS_PENDING = "PENDING"; 10 | public static final String STATUS_VERIFIED = "VERIFIED"; 11 | 12 | private Long id; 13 | private String token; 14 | private String status; 15 | private LocalDateTime expiredDateTime; 16 | private LocalDateTime issuedDateTime; 17 | private LocalDateTime confirmedDateTime; 18 | private User user; 19 | 20 | public VerificationToken(){ 21 | this.token = UUID.randomUUID().toString(); 22 | this.issuedDateTime = LocalDateTime.now(); 23 | this.expiredDateTime = this.issuedDateTime.plusDays(1); 24 | this.status = STATUS_PENDING; 25 | } 26 | 27 | @Id 28 | @GeneratedValue(strategy = GenerationType.AUTO) 29 | public Long getId() { 30 | return id; 31 | } 32 | 33 | public void setId(Long id) { 34 | this.id = id; 35 | } 36 | 37 | public String getToken() { 38 | return token; 39 | } 40 | 41 | public void setToken(String token) { 42 | this.token = token; 43 | } 44 | 45 | public String getStatus() { 46 | return status; 47 | } 48 | 49 | public void setStatus(String status) { 50 | this.status = status; 51 | } 52 | 53 | public LocalDateTime getExpiredDateTime() { 54 | return expiredDateTime; 55 | } 56 | 57 | public void setExpiredDateTime(LocalDateTime expiredDateTime) { 58 | this.expiredDateTime = expiredDateTime; 59 | } 60 | 61 | public LocalDateTime getIssuedDateTime() { 62 | return issuedDateTime; 63 | } 64 | 65 | public void setIssuedDateTime(LocalDateTime issuedDateTime) { 66 | this.issuedDateTime = issuedDateTime; 67 | } 68 | 69 | public LocalDateTime getConfirmedDateTime() { 70 | return confirmedDateTime; 71 | } 72 | 73 | public void setConfirmedDateTime(LocalDateTime confirmedDateTime) { 74 | this.confirmedDateTime = confirmedDateTime; 75 | } 76 | 77 | @OneToOne(cascade = CascadeType.ALL) 78 | @JoinColumn(name = "user_id") 79 | public User getUser() { 80 | return user; 81 | } 82 | 83 | public void setUser(User user) { 84 | this.user = user; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /app/src/main/java/com/hellokoding/account/repository/UserRepository.java: -------------------------------------------------------------------------------- 1 | package com.hellokoding.account.repository; 2 | 3 | import com.hellokoding.account.model.User; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | import java.util.List; 7 | 8 | public interface UserRepository extends JpaRepository { 9 | List findByEmail(String email); 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/com/hellokoding/account/repository/VerificationTokenRepository.java: -------------------------------------------------------------------------------- 1 | package com.hellokoding.account.repository; 2 | 3 | import com.hellokoding.account.model.VerificationToken; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | import java.util.List; 7 | 8 | public interface VerificationTokenRepository extends JpaRepository { 9 | List findByUserEmail(String email); 10 | List findByToken(String token); 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/hellokoding/account/service/SendingMailService.java: -------------------------------------------------------------------------------- 1 | package com.hellokoding.account.service; 2 | 3 | import com.hellokoding.account.model.MailProperties; 4 | import freemarker.template.Configuration; 5 | import freemarker.template.Template; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.stereotype.Service; 8 | import org.springframework.ui.freemarker.FreeMarkerTemplateUtils; 9 | 10 | import javax.mail.Message; 11 | import javax.mail.Session; 12 | import javax.mail.Transport; 13 | import javax.mail.internet.InternetAddress; 14 | import javax.mail.internet.MimeMessage; 15 | import java.util.HashMap; 16 | import java.util.Map; 17 | import java.util.Properties; 18 | import java.util.logging.Level; 19 | import java.util.logging.Logger; 20 | 21 | @Service 22 | public class SendingMailService { 23 | private final MailProperties mailProperties; 24 | private final Configuration templates; 25 | 26 | @Autowired 27 | SendingMailService(MailProperties mailProperties, Configuration templates){ 28 | this.mailProperties = mailProperties; 29 | this.templates = templates; 30 | } 31 | 32 | public boolean sendVerificationMail(String toEmail, String verificationCode) { 33 | String subject = "Please verify your email"; 34 | String body = ""; 35 | try { 36 | Template t = templates.getTemplate("email-verification.ftl"); 37 | Map map = new HashMap<>(); 38 | map.put("VERIFICATION_URL", mailProperties.getVerificationapi() + verificationCode); 39 | body = FreeMarkerTemplateUtils.processTemplateIntoString(t, map); 40 | } catch (Exception ex) { 41 | Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, ex.getMessage(), ex); 42 | } 43 | return sendMail(toEmail, subject, body); 44 | } 45 | 46 | private boolean sendMail(String toEmail, String subject, String body) { 47 | try { 48 | Properties props = System.getProperties(); 49 | props.put("mail.transport.protocol", "smtp"); 50 | props.put("mail.smtp.port", mailProperties.getSmtp().getPort()); 51 | props.put("mail.smtp.starttls.enable", "true"); 52 | props.put("mail.smtp.auth", "true"); 53 | 54 | Session session = Session.getDefaultInstance(props); 55 | session.setDebug(true); 56 | 57 | MimeMessage msg = new MimeMessage(session); 58 | msg.setFrom(new InternetAddress(mailProperties.getFrom(), mailProperties.getFromName())); 59 | msg.setRecipient(Message.RecipientType.TO, new InternetAddress(toEmail)); 60 | msg.setSubject(subject); 61 | msg.setContent(body, "text/html"); 62 | 63 | Transport transport = session.getTransport(); 64 | transport.connect(mailProperties.getSmtp().getHost(), mailProperties.getSmtp().getUsername(), mailProperties.getSmtp().getPassword()); 65 | transport.sendMessage(msg, msg.getAllRecipients()); 66 | return true; 67 | } catch (Exception ex) { 68 | Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, ex.getMessage(), ex); 69 | } 70 | 71 | return false; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /app/src/main/java/com/hellokoding/account/service/VerificationTokenService.java: -------------------------------------------------------------------------------- 1 | package com.hellokoding.account.service; 2 | 3 | import com.hellokoding.account.model.User; 4 | import com.hellokoding.account.model.VerificationToken; 5 | import com.hellokoding.account.repository.UserRepository; 6 | import com.hellokoding.account.repository.VerificationTokenRepository; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.http.ResponseEntity; 9 | import org.springframework.stereotype.Service; 10 | 11 | import java.time.LocalDateTime; 12 | import java.util.List; 13 | 14 | @Service 15 | public class VerificationTokenService { 16 | private UserRepository userRepository; 17 | private VerificationTokenRepository verificationTokenRepository; 18 | private SendingMailService sendingMailService; 19 | 20 | @Autowired 21 | public VerificationTokenService(UserRepository userRepository, VerificationTokenRepository verificationTokenRepository, SendingMailService sendingMailService){ 22 | this.userRepository = userRepository; 23 | this.verificationTokenRepository = verificationTokenRepository; 24 | this.sendingMailService = sendingMailService; 25 | } 26 | 27 | public void createVerification(String email){ 28 | List users = userRepository.findByEmail(email); 29 | User user; 30 | if (users.isEmpty()) { 31 | user = new User(); 32 | user.setEmail(email); 33 | userRepository.save(user); 34 | } else { 35 | user = users.get(0); 36 | } 37 | 38 | List verificationTokens = verificationTokenRepository.findByUserEmail(email); 39 | VerificationToken verificationToken; 40 | if (verificationTokens.isEmpty()) { 41 | verificationToken = new VerificationToken(); 42 | verificationToken.setUser(user); 43 | verificationTokenRepository.save(verificationToken); 44 | } else { 45 | verificationToken = verificationTokens.get(0); 46 | } 47 | 48 | sendingMailService.sendVerificationMail(email, verificationToken.getToken()); 49 | } 50 | 51 | public ResponseEntity verifyEmail(String token){ 52 | List verificationTokens = verificationTokenRepository.findByToken(token); 53 | if (verificationTokens.isEmpty()) { 54 | return ResponseEntity.badRequest().body("Invalid token."); 55 | } 56 | 57 | VerificationToken verificationToken = verificationTokens.get(0); 58 | if (verificationToken.getExpiredDateTime().isBefore(LocalDateTime.now())) { 59 | return ResponseEntity.unprocessableEntity().body("Expired token."); 60 | } 61 | 62 | verificationToken.setConfirmedDateTime(LocalDateTime.now()); 63 | verificationToken.setStatus(VerificationToken.STATUS_VERIFIED); 64 | verificationToken.getUser().setIsActive(true); 65 | verificationTokenRepository.save(verificationToken); 66 | 67 | return ResponseEntity.ok("You have successfully verified your email address."); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /app/src/main/java/com/hellokoding/account/web/AccountController.java: -------------------------------------------------------------------------------- 1 | package com.hellokoding.account.web; 2 | 3 | import com.hellokoding.account.model.VerificationForm; 4 | import com.hellokoding.account.service.VerificationTokenService; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Controller; 7 | import org.springframework.ui.Model; 8 | import org.springframework.validation.BindingResult; 9 | import org.springframework.web.bind.annotation.GetMapping; 10 | import org.springframework.web.bind.annotation.PostMapping; 11 | import org.springframework.web.bind.annotation.ResponseBody; 12 | 13 | import javax.validation.Valid; 14 | 15 | @Controller 16 | public class AccountController { 17 | @Autowired 18 | VerificationTokenService verificationTokenService; 19 | 20 | @GetMapping("/") 21 | public String index() { 22 | return "redirect:/email-verification"; 23 | } 24 | 25 | @GetMapping("/email-verification") 26 | public String formGet(Model model) { 27 | model.addAttribute("verificationForm", new VerificationForm()); 28 | return "verification-form"; 29 | } 30 | 31 | @PostMapping("/email-verification") 32 | public String formPost(@Valid VerificationForm verificationForm, BindingResult bindingResult, Model model) { 33 | if (!bindingResult.hasErrors()) { 34 | model.addAttribute("noErrors", true); 35 | } 36 | model.addAttribute("verificationForm", verificationForm); 37 | 38 | verificationTokenService.createVerification(verificationForm.getEmail()); 39 | return "verification-form"; 40 | } 41 | 42 | @GetMapping("/verify-email") 43 | @ResponseBody 44 | public String verifyEmail(String code) { 45 | return verificationTokenService.verifyEmail(code).getBody(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.freemarker.template-loader-path: classpath:/templates 2 | spring.freemarker.suffix: .ftl 3 | 4 | spring.datasource.url=jdbc:mysql://mysql:3306/account?useSSL=false 5 | spring.datasource.username=root 6 | spring.datasource.password=hellokoding 7 | spring.datasource.driver-class-name=com.mysql.jdbc.Driver 8 | 9 | spring.jpa.hibernate.ddl-auto=create 10 | spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect 11 | spring.jpa.generate-ddl=true 12 | spring.jpa.show-sql=true 13 | 14 | mail.smtp.host={YOUR_SES_SMTP_HOST} 15 | mail.smtp.port={YOUR_SES_SMTP_PORT} 16 | mail.smtp.username={YOUR_SES_SMTP_USERNAME} 17 | mail.smtp.password={YOUR_SES_SMTP_PASSWORD} 18 | mail.from={YOUR_FROM_EMAIL} 19 | mail.from-name={YOUR_FROM_NAME} 20 | 21 | mail.verificationapi=http://localhost/verify-email?code= -------------------------------------------------------------------------------- /app/src/main/resources/static/css/main.css: -------------------------------------------------------------------------------- 1 | .hello-title{ 2 | color: darkgreen; 3 | } -------------------------------------------------------------------------------- /app/src/main/resources/static/js/main.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | console.log("Hello World!"); 3 | })(); -------------------------------------------------------------------------------- /app/src/main/resources/templates/email-verification.ftl: -------------------------------------------------------------------------------- 1 | Hi Giau,
2 | 3 | Thanks for using Hello Koding! Please confirm your email address by clicking on the link below.
4 | 5 | ${VERIFICATION_URL}
6 | 7 | If you did not sign up for a Hello Koding account please disregard this email.
8 | 9 | The Hello Koding team, -------------------------------------------------------------------------------- /app/src/main/resources/templates/verification-form.ftl: -------------------------------------------------------------------------------- 1 | <#import "/spring.ftl" as spring /> 2 | 3 | 4 | 5 | 6 | 7 | Activate account with Spring Boot, MongoDB, NGINX, Docker Compose 8 | 9 | 10 |

Verify your email

11 | 12 | <@spring.bind "verificationForm"/> 13 | <#if verificationForm?? && noErrors??> 14 | Sent a confirmation link to your inbox ${verificationForm.email}
15 | <#else> 16 |
17 | Email:
18 | <@spring.formInput "verificationForm.email"/> 19 | <@spring.showErrors "
"/> 20 |

21 | 22 |
23 | 24 | 25 | -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | nginx: 4 | container_name: some-nginx 5 | image: nginx:1.13 6 | restart: always 7 | ports: 8 | - 80:80 9 | - 443:443 10 | volumes: 11 | - ./nginx/conf.d:/etc/nginx/conf.d 12 | depends_on: 13 | - app 14 | 15 | mysql: 16 | container_name: some-mysql 17 | image: mysql/mysql-server:5.7 18 | environment: 19 | MYSQL_ROOT_PASSWORD: hellokoding 20 | MYSQL_ROOT_HOST: '%' 21 | MYSQL_DATABASE: account 22 | ports: 23 | - "3306:3306" 24 | restart: always 25 | 26 | app: 27 | restart: always 28 | build: ./app 29 | working_dir: /app 30 | volumes: 31 | - ./app:/app 32 | - ~/.m2:/root/.m2 33 | expose: 34 | - "8080" 35 | command: mvn clean spring-boot:run 36 | depends_on: 37 | - mysql 38 | -------------------------------------------------------------------------------- /nginx/conf.d/app.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | charset utf-8; 4 | access_log off; 5 | 6 | location / { 7 | proxy_pass http://app:8080; 8 | proxy_set_header Host $host:$server_port; 9 | proxy_set_header X-Forwarded-Host $server_name; 10 | proxy_set_header X-Real-IP $remote_addr; 11 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 12 | } 13 | 14 | location /static { 15 | access_log off; 16 | expires 30d; 17 | 18 | alias /app/static; 19 | } 20 | } 21 | --------------------------------------------------------------------------------