Skip to content

Commit

Permalink
Add register and login page
Browse files Browse the repository at this point in the history
  • Loading branch information
jgarivera committed Aug 14, 2024
1 parent 00450f7 commit 6a00e92
Show file tree
Hide file tree
Showing 6 changed files with 446 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.jgarivera.qwest.auth.presentation;

import com.jgarivera.qwest.auth.domain.User;
import com.jgarivera.qwest.auth.domain.UserRepository;
import com.jgarivera.qwest.shared.UUIDFactory;
import jakarta.validation.Valid;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;

@Controller("auth")
class PageController {

private final UserRepository repository;
private final UUIDFactory uuidFactory;
private final PasswordEncoder passwordEncoder;

PageController(UserRepository repository, UUIDFactory uuidFactory, PasswordEncoder passwordEncoder) {
this.repository = repository;
this.uuidFactory = uuidFactory;
this.passwordEncoder = passwordEncoder;
}

@GetMapping("/login")
String login() {
return "pages/login";
}

@GetMapping("/register")
String register(@ModelAttribute("form") RegistrationForm form) {
return "pages/register";
}

@PostMapping("/register")
String register(@Valid @ModelAttribute("form") RegistrationForm form, BindingResult result) {
if (result.hasErrors()) {
return "pages/register";
}

User user = form.toUser(uuidFactory, passwordEncoder);
repository.save(user);

return "redirect:/login?registered";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package com.jgarivera.qwest.auth.presentation;

import com.jgarivera.qwest.auth.domain.EmailAddress;
import com.jgarivera.qwest.auth.domain.PersonalName;
import com.jgarivera.qwest.auth.domain.User;
import com.jgarivera.qwest.auth.domain.UserId;
import com.jgarivera.qwest.auth.domain.Username;
import com.jgarivera.qwest.shared.UUIDFactory;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import org.springframework.security.crypto.password.PasswordEncoder;

class RegistrationForm {

@NotBlank(message = "First name must not be blank")
private String firstName;
private String middleName;
@NotBlank(message = "Last name must not be blank")
private String lastName;

@NotBlank(message = "Email must not be blank")
@Pattern(
regexp = EmailAddress.VALID_EMAIL_ADDRESS_REGEX,
message = "Email address must be valid"
)
private String email;

@NotBlank(message = "Username must not be blank")
@Pattern(
regexp = Username.VALID_USERNAME_REGEX,
message = "Username must only contain letters, numbers, underscores, and be between 8 to 30 characters"
)
private String username;

@NotBlank(message = "Password must not be blank")
private String password;

public User toUser(UUIDFactory uuidFactory, PasswordEncoder passwordEncoder) {
return new User(
new UserId(uuidFactory.create()),
new PersonalName(firstName, middleName, lastName),
new EmailAddress(email),
new Username(username),
passwordEncoder.encode(password)
);
}

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getMiddleName() {
return middleName;
}

public void setMiddleName(String middleName) {
this.middleName = middleName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}
}
57 changes: 57 additions & 0 deletions src/main/resources/templates/pages/login.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layouts/default}">
<head>
<title>Log in</title>
</head>

<body>
<section layout:fragment="~{app-content}">
<div class="content">
<h1>Log in</h1>
<p>
Welcome back!
</p>
<p class="is-size-7">
Are you new here? Register <a th:href="@{/register}">here</a>!
</p>

<div class="container is-max-desktop">
<div th:if="${param.registered}" class="notification is-success">
You have successfully registered! You may now log in.
</div>
<div th:if="${param.error}" class="notification is-danger">
There was an error with your login. Please make sure your username and password are correct.
</div>

<form method="post" th:action="@{/login}" class="form-horizontal">
<fieldset>
<div class="field">
<label class="label" for="username">Username</label>
<div class="control">
<input id="username" name="username" type="text" class="input">
</div>
</div>

<div class="field">
<label class="label" for="password">Password</label>
<div class="control">
<input id="password" name="password" type="password" class="input">
</div>
</div>

<div class="field">
<div class="control">
<button type="submit" class="button is-primary">Log in</button>
</div>
</div>
</fieldset>
</form>
</div>
</div>
</section>
</body>

</html>
100 changes: 100 additions & 0 deletions src/main/resources/templates/pages/register.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layouts/default}">
<head>
<title>Register</title>
</head>

<body>
<section layout:fragment="~{app-content}">
<div class="content">
<h1>Register an account</h1>
<p>
Your questing begins here!
</p>

<div class="container is-max-desktop">
<form method="post" th:action="@{/register}" th:object="${form}" class="form-horizontal">
<fieldset>
<div class="columns">
<div class="column">
<div class="field">
<label class="label" for="firstName">First Name</label>
<div class="control">
<input id="firstName" name="firstName" type="text" class="input"
th:classappend="${#fields.hasErrors('firstName')} ? is-danger"
th:field="*{firstName}">
</div>
<p class="help is-danger" th:if="${#fields.hasErrors('firstName')}"
th:errors="*{firstName}"></p>
</div>
</div>
<div class="column">
<div class="field">
<label class="label" for="middleName">Middle Name</label>
<div class="control">
<input id="middleName" name="middleName" type="text" class="input"
th:classappend="${#fields.hasErrors('middleName')} ? is-danger"
th:field="*{middleName}">
</div>
<p class="help is-danger" th:if="${#fields.hasErrors('middleName')}"
th:errors="*{middleName}"></p>
</div>
</div>
<div class="column">
<div class="field">
<label class="label" for="lastName">Last Name</label>
<div class="control">
<input id="lastName" name="lastName" type="text" class="input"
th:classappend="${#fields.hasErrors('lastName')} ? is-danger"
th:field="*{lastName}">
</div>
<p class="help is-danger" th:if="${#fields.hasErrors('lastName')}"
th:errors="*{lastName}"></p>
</div>
</div>
</div>

<div class="field">
<label class="label" for="email">Email Address</label>
<div class="control">
<input id="email" name="email" type="text" class="input"
th:classappend="${#fields.hasErrors('email')} ? is-danger" th:field="*{email}">
</div>
<p class="help is-danger" th:if="${#fields.hasErrors('email')}" th:errors="*{email}"></p>
</div>

<div class="field">
<label class="label" for="username">Username</label>
<div class="control">
<input id="username" name="username" type="text" class="input"
th:classappend="${#fields.hasErrors('username')} ? is-danger" th:field="*{username}">
</div>
<p class="help is-danger" th:if="${#fields.hasErrors('username')}" th:errors="*{username}"></p>
</div>

<div class="field">
<label class="label" for="password">Password</label>
<div class="control">
<input id="password" name="password" type="password" class="input"
th:classappend="${#fields.hasErrors('password')} ? is-danger" th:field="*{password}">
</div>
<p class="help is-danger" th:if="${#fields.hasErrors('password')}" th:errors="*{password}"></p>
</div>

<div class="field">
<div class="control">
<button type="submit" class="button is-primary">Register</button>
</div>
</div>

</fieldset>
</form>
</div>
</div>
</section>
</body>

</html>
Loading

0 comments on commit 6a00e92

Please sign in to comment.