Lab 6 Jwt start
This commit is contained in:
parent
c98e598675
commit
fed78e90b7
build.gradle
src/main
java/com/example/demo
configuration
OpenAPI30Configuration.javaPasswordEncoderConfiguration.javaSecurityConfiguration.javaWebConfiguration.java
jwt
master
Master.javaMasterController.javaMasterMvcController.javaMasterRepository.javaMasterRole.javaMasterService.javaMasterSignupDto.java
order
product
util/validation
resources/templates
@ -17,6 +17,7 @@ jar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-web'
|
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
|
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
|
||||||
implementation 'org.springframework.boot:spring-boot-devtools'
|
implementation 'org.springframework.boot:spring-boot-devtools'
|
||||||
@ -27,6 +28,8 @@ dependencies {
|
|||||||
implementation 'org.webjars:font-awesome:6.1.0'
|
implementation 'org.webjars:font-awesome:6.1.0'
|
||||||
|
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-security'
|
||||||
|
implementation 'com.auth0:java-jwt:4.4.0'
|
||||||
implementation 'com.h2database:h2:2.1.210'
|
implementation 'com.h2database:h2:2.1.210'
|
||||||
|
|
||||||
implementation 'org.hibernate.validator:hibernate-validator'
|
implementation 'org.hibernate.validator:hibernate-validator'
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
package com.example.demo.configuration;
|
||||||
|
|
||||||
|
|
||||||
|
import com.example.demo.configuration.jwt.JwtFilter;
|
||||||
|
import io.swagger.v3.oas.models.Components;
|
||||||
|
import io.swagger.v3.oas.models.OpenAPI;
|
||||||
|
import io.swagger.v3.oas.models.security.SecurityRequirement;
|
||||||
|
import io.swagger.v3.oas.models.security.SecurityScheme;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class OpenAPI30Configuration {
|
||||||
|
public static final String API_PREFIX = "/api/1.0";
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public OpenAPI customizeOpenAPI() {
|
||||||
|
final String securitySchemeName = JwtFilter.TOKEN_BEGIN_STR;
|
||||||
|
return new OpenAPI()
|
||||||
|
.addSecurityItem(new SecurityRequirement()
|
||||||
|
.addList(securitySchemeName))
|
||||||
|
.components(new Components()
|
||||||
|
.addSecuritySchemes(securitySchemeName, new SecurityScheme()
|
||||||
|
.name(securitySchemeName)
|
||||||
|
.type(SecurityScheme.Type.HTTP)
|
||||||
|
.scheme("bearer")
|
||||||
|
.bearerFormat("JWT")));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package com.example.demo.configuration;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class PasswordEncoderConfiguration {
|
||||||
|
@Bean
|
||||||
|
public PasswordEncoder passwordEncoder() {
|
||||||
|
return new BCryptPasswordEncoder();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,92 @@
|
|||||||
|
package com.example.demo.configuration;
|
||||||
|
|
||||||
|
|
||||||
|
import com.example.demo.configuration.jwt.JwtFilter;
|
||||||
|
import com.example.demo.master.MasterController;
|
||||||
|
import com.example.demo.master.MasterRole;
|
||||||
|
import com.example.demo.master.MasterService;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
|
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||||
|
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
|
||||||
|
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||||
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
@EnableMethodSecurity(
|
||||||
|
securedEnabled = true
|
||||||
|
)
|
||||||
|
public class SecurityConfiguration {
|
||||||
|
private final Logger log = LoggerFactory.getLogger(SecurityConfiguration.class);
|
||||||
|
public static final String SPA_URL_MASK = "/{path:[^\\.]*}";
|
||||||
|
private final MasterService masterService;
|
||||||
|
private final JwtFilter jwtFilter;
|
||||||
|
|
||||||
|
public SecurityConfiguration(MasterService masterService) {
|
||||||
|
this.masterService = masterService;
|
||||||
|
this.jwtFilter = new JwtFilter(masterService);
|
||||||
|
createAdminOnStartup();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createAdminOnStartup() {
|
||||||
|
final String admin = "admin";
|
||||||
|
if (masterService.findMaster(admin) == null) {
|
||||||
|
log.info("Admin user successfully created");
|
||||||
|
masterService.addMaster(admin, admin, admin, admin, MasterRole.ADMIN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
|
http.cors()
|
||||||
|
.and()
|
||||||
|
.csrf().disable()
|
||||||
|
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||||
|
.and()
|
||||||
|
.authorizeHttpRequests()
|
||||||
|
.requestMatchers("/", SPA_URL_MASK).permitAll()
|
||||||
|
.requestMatchers(HttpMethod.POST, MasterController.URL_LOGIN).permitAll()
|
||||||
|
.requestMatchers(HttpMethod.POST, MasterController.URL_SING_UP).permitAll()
|
||||||
|
.requestMatchers(HttpMethod.POST, MasterController.URL_WHO_AM_I).permitAll()
|
||||||
|
.anyRequest()
|
||||||
|
.authenticated()
|
||||||
|
.and()
|
||||||
|
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
|
||||||
|
.anonymous();
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public AuthenticationManager authenticationManagerBean(HttpSecurity http) throws Exception {
|
||||||
|
AuthenticationManagerBuilder authenticationManagerBuilder = http
|
||||||
|
.getSharedObject(AuthenticationManagerBuilder.class);
|
||||||
|
authenticationManagerBuilder.userDetailsService(masterService);
|
||||||
|
return authenticationManagerBuilder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public WebSecurityCustomizer webSecurityCustomizer() {
|
||||||
|
return (web) -> web.ignoring()
|
||||||
|
.requestMatchers(HttpMethod.OPTIONS, "/**")
|
||||||
|
.requestMatchers("/*.js")
|
||||||
|
.requestMatchers("/*.html")
|
||||||
|
.requestMatchers("/*.css")
|
||||||
|
.requestMatchers("/assets/**")
|
||||||
|
.requestMatchers("/favicon.ico")
|
||||||
|
.requestMatchers("/.js", "/.css")
|
||||||
|
.requestMatchers("/swagger-ui/index.html")
|
||||||
|
.requestMatchers("/webjars/**")
|
||||||
|
.requestMatchers("/swagger-resources/**")
|
||||||
|
.requestMatchers("/v3/api-docs/**")
|
||||||
|
.requestMatchers("/h2-console");
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package com.example.demo;
|
package com.example.demo.configuration;
|
||||||
|
|
||||||
import org.springframework.boot.web.server.ErrorPage;
|
import org.springframework.boot.web.server.ErrorPage;
|
||||||
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
|
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
|
||||||
@ -12,23 +12,19 @@ import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
|
|||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
@Configuration
|
@Configuration
|
||||||
public class WebConfiguration implements WebMvcConfigurer {
|
public class WebConfiguration implements WebMvcConfigurer {
|
||||||
public static final String REST_API = "/api";
|
|
||||||
@Override
|
|
||||||
public void addCorsMappings(CorsRegistry registry) {
|
|
||||||
registry.addMapping("/**").allowedMethods("*");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addViewControllers(ViewControllerRegistry registry) {
|
public void addViewControllers(ViewControllerRegistry registry) {
|
||||||
ViewControllerRegistration registration = registry.addViewController("/notFound");
|
registry.addViewController(SecurityConfiguration.SPA_URL_MASK).setViewName("forward:/");
|
||||||
registration.setViewName("forward:/index.html");
|
registry.addViewController("/notFound").setViewName("forward:/");
|
||||||
registration.setStatusCode(HttpStatus.OK);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> containerCustomizer() {
|
public WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> containerCustomizer() {
|
||||||
return container -> {
|
return container -> container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/notFound"));
|
||||||
container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/notFound"));
|
}
|
||||||
};
|
|
||||||
|
@Override
|
||||||
|
public void addCorsMappings(CorsRegistry registry) {
|
||||||
|
registry.addMapping("/**").allowedMethods("*");
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package com.example.demo.configuration.jwt;
|
||||||
|
|
||||||
|
public class JwtException extends RuntimeException {
|
||||||
|
public JwtException(Throwable throwable) {
|
||||||
|
super(throwable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JwtException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
package com.example.demo.configuration.jwt;
|
||||||
|
|
||||||
|
import com.example.demo.master.MasterService;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import jakarta.servlet.FilterChain;
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
|
import jakarta.servlet.ServletRequest;
|
||||||
|
import jakarta.servlet.ServletResponse;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.web.filter.GenericFilterBean;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class JwtFilter extends GenericFilterBean {
|
||||||
|
private static final String AUTHORIZATION = "Authorization";
|
||||||
|
public static final String TOKEN_BEGIN_STR = "Bearer ";
|
||||||
|
|
||||||
|
private final MasterService masterService;
|
||||||
|
|
||||||
|
public JwtFilter(MasterService masterService) {
|
||||||
|
this.masterService = masterService;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getTokenFromRequest(HttpServletRequest request) {
|
||||||
|
String bearer = request.getHeader(AUTHORIZATION);
|
||||||
|
if (StringUtils.hasText(bearer) && bearer.startsWith(TOKEN_BEGIN_STR)) {
|
||||||
|
return bearer.substring(TOKEN_BEGIN_STR.length());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void raiseException(ServletResponse response, int status, String message) throws IOException {
|
||||||
|
if (response instanceof final HttpServletResponse httpResponse) {
|
||||||
|
httpResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
||||||
|
httpResponse.setStatus(status);
|
||||||
|
final byte[] body = new ObjectMapper().writeValueAsBytes(message);
|
||||||
|
response.getOutputStream().write(body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doFilter(ServletRequest request,
|
||||||
|
ServletResponse response,
|
||||||
|
FilterChain chain) throws IOException, ServletException {
|
||||||
|
if (request instanceof final HttpServletRequest httpRequest) {
|
||||||
|
final String token = getTokenFromRequest(httpRequest);
|
||||||
|
if (StringUtils.hasText(token)) {
|
||||||
|
try {
|
||||||
|
final UserDetails user = masterService.loadUserByToken(token);
|
||||||
|
final UsernamePasswordAuthenticationToken auth =
|
||||||
|
new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(auth);
|
||||||
|
} catch (JwtException e) {
|
||||||
|
raiseException(response, HttpServletResponse.SC_UNAUTHORIZED, e.getMessage());
|
||||||
|
return;
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
raiseException(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
|
||||||
|
String.format("Internal error: %s", e.getMessage()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chain.doFilter(request, response);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package com.example.demo.configuration.jwt;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@ConfigurationProperties(prefix = "jwt", ignoreInvalidFields = true)
|
||||||
|
public class JwtProperties {
|
||||||
|
private String devToken = "";
|
||||||
|
private Boolean isDev = true;
|
||||||
|
|
||||||
|
public String getDevToken() {
|
||||||
|
return devToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDevToken(String devToken) {
|
||||||
|
this.devToken = devToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean isDev() {
|
||||||
|
return isDev;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDev(Boolean dev) {
|
||||||
|
isDev = dev;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,107 @@
|
|||||||
|
package com.example.demo.configuration.jwt;
|
||||||
|
|
||||||
|
import com.auth0.jwt.JWT;
|
||||||
|
import com.auth0.jwt.algorithms.Algorithm;
|
||||||
|
import com.auth0.jwt.exceptions.JWTVerificationException;
|
||||||
|
import com.auth0.jwt.interfaces.DecodedJWT;
|
||||||
|
import com.auth0.jwt.interfaces.JWTVerifier;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class JwtProvider {
|
||||||
|
private final static Logger LOG = LoggerFactory.getLogger(JwtProvider.class);
|
||||||
|
|
||||||
|
private final static byte[] HEX_ARRAY = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII);
|
||||||
|
private final static String ISSUER = "auth0";
|
||||||
|
|
||||||
|
private final Algorithm algorithm;
|
||||||
|
private final JWTVerifier verifier;
|
||||||
|
|
||||||
|
public JwtProvider(JwtProperties jwtProperties) {
|
||||||
|
if (!jwtProperties.isDev()) {
|
||||||
|
LOG.info("Generate new JWT key for prod");
|
||||||
|
try {
|
||||||
|
final MessageDigest salt = MessageDigest.getInstance("SHA-256");
|
||||||
|
salt.update(UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8));
|
||||||
|
LOG.info("Use generated JWT key for prod \n{}", bytesToHex(salt.digest()));
|
||||||
|
algorithm = Algorithm.HMAC256(bytesToHex(salt.digest()));
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new JwtException(e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG.info("Use default JWT key for dev \n{}", jwtProperties.getDevToken());
|
||||||
|
algorithm = Algorithm.HMAC256(jwtProperties.getDevToken());
|
||||||
|
}
|
||||||
|
verifier = JWT.require(algorithm)
|
||||||
|
.withIssuer(ISSUER)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String bytesToHex(byte[] bytes) {
|
||||||
|
byte[] hexChars = new byte[bytes.length * 2];
|
||||||
|
for (int j = 0; j < bytes.length; j++) {
|
||||||
|
int v = bytes[j] & 0xFF;
|
||||||
|
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
|
||||||
|
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
|
||||||
|
}
|
||||||
|
return new String(hexChars, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String generateToken(String login) {
|
||||||
|
final Date issueDate = Date.from(LocalDate.now()
|
||||||
|
.atStartOfDay(ZoneId.systemDefault())
|
||||||
|
.toInstant());
|
||||||
|
final Date expireDate = Date.from(LocalDate.now()
|
||||||
|
.plusDays(15)
|
||||||
|
.atStartOfDay(ZoneId.systemDefault())
|
||||||
|
.toInstant());
|
||||||
|
return JWT.create()
|
||||||
|
.withIssuer(ISSUER)
|
||||||
|
.withIssuedAt(issueDate)
|
||||||
|
.withExpiresAt(expireDate)
|
||||||
|
.withSubject(login)
|
||||||
|
.sign(algorithm);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DecodedJWT validateToken(String token) {
|
||||||
|
try {
|
||||||
|
return verifier.verify(token);
|
||||||
|
} catch (JWTVerificationException e) {
|
||||||
|
throw new JwtException(String.format("Token verification error: %s", e.getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isTokenValid(String token) {
|
||||||
|
if (!StringUtils.hasText(token)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
validateToken(token);
|
||||||
|
return true;
|
||||||
|
} catch (JwtException e) {
|
||||||
|
LOG.error(e.getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<String> getLoginFromToken(String token) {
|
||||||
|
try {
|
||||||
|
return Optional.ofNullable(validateToken(token).getSubject());
|
||||||
|
} catch (JwtException e) {
|
||||||
|
LOG.error(e.getMessage());
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,8 @@
|
|||||||
package com.example.demo.master;
|
package com.example.demo.master;
|
||||||
|
|
||||||
import com.example.demo.order.Order;
|
import com.example.demo.master.MasterRole;
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@ -19,14 +18,17 @@ public class Master {
|
|||||||
private String email;
|
private String email;
|
||||||
private String password;
|
private String password;
|
||||||
|
|
||||||
|
private MasterRole role;
|
||||||
|
|
||||||
public Master() {
|
public Master() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Master(String firstName, String lastName, String email, String password) {
|
public Master(String firstName, String lastName, String email, String password, MasterRole role) {
|
||||||
this.firstName = firstName;
|
this.firstName = firstName;
|
||||||
this.lastName = lastName;
|
this.lastName = lastName;
|
||||||
this.email = email;
|
this.email = email;
|
||||||
this.password = password;
|
this.password = password;
|
||||||
|
this.role = role;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Long getId() {
|
public Long getId() {
|
||||||
@ -45,6 +47,10 @@ public class Master {
|
|||||||
|
|
||||||
public String getPassword() { return password; }
|
public String getPassword() { return password; }
|
||||||
|
|
||||||
|
public MasterRole getRole() {
|
||||||
|
return role;
|
||||||
|
}
|
||||||
|
|
||||||
public void setFirstName(String firstName) {
|
public void setFirstName(String firstName) {
|
||||||
this.firstName = firstName;
|
this.firstName = firstName;
|
||||||
}
|
}
|
||||||
@ -61,6 +67,10 @@ public class Master {
|
|||||||
this.password = password;
|
this.password = password;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setRole(MasterRole role) {
|
||||||
|
this.role = role;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
|
@ -1,74 +1,75 @@
|
|||||||
package com.example.demo.master;
|
package com.example.demo.master;
|
||||||
|
|
||||||
import com.example.demo.WebConfiguration;
|
import com.example.demo.configuration.OpenAPI30Configuration;
|
||||||
|
import com.example.demo.order.Order;
|
||||||
import com.example.demo.order.OrderService;
|
import com.example.demo.order.OrderService;
|
||||||
import com.example.demo.product.ProductDto;
|
import com.example.demo.util.validation.ValidationException;
|
||||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
import jakarta.validation.Valid;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
import org.springframework.web.bind.annotation.PatchMapping;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
|
import java.security.Principal;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping(WebConfiguration.REST_API + "/master")
|
|
||||||
public class MasterController {
|
public class MasterController {
|
||||||
private final MasterService masterService;
|
private final MasterService masterService;
|
||||||
private final OrderService orderService;
|
private final OrderService orderService;
|
||||||
|
|
||||||
|
public static final String URL_LOGIN = "/jwt/login";
|
||||||
|
public static final String URL_SING_UP = "/sing_up";
|
||||||
|
public static final String URL_WHO_AM_I = "/who_am_i";
|
||||||
|
|
||||||
public MasterController(MasterService masterService, OrderService orderService) {
|
public MasterController(MasterService masterService, OrderService orderService) {
|
||||||
this.masterService = masterService;
|
this.masterService = masterService;
|
||||||
this.orderService = orderService;
|
this.orderService = orderService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/")
|
@PostMapping(URL_LOGIN)
|
||||||
public MasterDto getCurrentMaster() {
|
public String login(@RequestBody @Valid MasterDto userDto) {
|
||||||
return new MasterDto(masterService.findMaster(masterService.getCurrentMasterId()));
|
return masterService.loginAndGetToken(userDto);
|
||||||
|
|
||||||
}
|
|
||||||
@GetMapping("/{email}/{password}")
|
|
||||||
public MasterDto getCurrentMaster(@PathVariable("email") String email,
|
|
||||||
@PathVariable("password") String password) {
|
|
||||||
var master = new MasterDto(masterService.findMaster(email, password));
|
|
||||||
masterService.setCurrentMasterId(master.getId());
|
|
||||||
return master;
|
|
||||||
}
|
|
||||||
@PostMapping("/")
|
|
||||||
public MasterDto createMaster(@RequestParam("firstName") String firstName,
|
|
||||||
@RequestParam("lastName") String lastName,
|
|
||||||
@RequestParam("email") String email,
|
|
||||||
@RequestParam("password") String password) {
|
|
||||||
MasterDto master = new MasterDto(masterService.addMaster(firstName, lastName, email, password));
|
|
||||||
masterService.setCurrentMasterId(master.getId());
|
|
||||||
orderService.addOrder(master.getId());
|
|
||||||
return master;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@PatchMapping("/")
|
@PostMapping(URL_SING_UP)
|
||||||
|
public String singUp(@RequestBody MasterSignupDto masterSignupDto) {
|
||||||
|
try {
|
||||||
|
final Master master = masterService.addMaster(masterSignupDto.getFirstName(), masterSignupDto.getLastName(),
|
||||||
|
masterSignupDto.getEmail(), masterSignupDto.getPassword(), MasterRole.USER);
|
||||||
|
final Order order = orderService.addOrder(master.getId());
|
||||||
|
return "created " + master.getEmail();
|
||||||
|
} catch (ValidationException e) {
|
||||||
|
return e.getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping(URL_WHO_AM_I)
|
||||||
|
public String whoAmI(@RequestParam("token") String token) {
|
||||||
|
UserDetails userDetails = masterService.loadUserByToken(token);
|
||||||
|
Master master = masterService.findMaster(userDetails.getUsername());
|
||||||
|
return master.getRole().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping(OpenAPI30Configuration.API_PREFIX + "/master")
|
||||||
|
public MasterDto getCurrentMaster(Principal principal) {
|
||||||
|
return new MasterDto(masterService.findMaster(principal.getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PatchMapping(OpenAPI30Configuration.API_PREFIX + "/master")
|
||||||
public MasterDto updateMaster(@RequestParam("firstName") String firstName,
|
public MasterDto updateMaster(@RequestParam("firstName") String firstName,
|
||||||
@RequestParam("lastName") String lastName,
|
@RequestParam("lastName") String lastName,
|
||||||
@RequestParam("email") String email,
|
@RequestParam("email") String email,
|
||||||
@RequestParam("password") String password) {
|
@RequestParam("password") String password,
|
||||||
return new MasterDto(masterService.updateMaster(masterService.getCurrentMasterId(),
|
Principal principal) {
|
||||||
|
return new MasterDto(masterService.updateMaster(masterService.findMaster(principal.getName()).getId(),
|
||||||
firstName, lastName, email, password));
|
firstName, lastName, email, password));
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/")
|
@DeleteMapping("/")
|
||||||
public MasterDto deleteMaster(@PathVariable Long id) {
|
public MasterDto deleteMaster(@PathVariable Long id) {
|
||||||
return new MasterDto(masterService.deleteMaster(masterService.getCurrentMasterId()));
|
return new MasterDto(masterService.deleteMaster(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/log_out")
|
@GetMapping(OpenAPI30Configuration.API_PREFIX + "/master/all")
|
||||||
public void logOut() {
|
|
||||||
masterService.setCurrentMasterId(0L);
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/all")
|
|
||||||
public List<MasterDto> GetMasters(){
|
public List<MasterDto> GetMasters(){
|
||||||
return masterService.findAllMasters().stream().map(MasterDto::new).toList();
|
return masterService.findAllMasters().stream().map(MasterDto::new).toList();
|
||||||
}
|
}
|
||||||
|
@ -1,84 +0,0 @@
|
|||||||
package com.example.demo.master;
|
|
||||||
|
|
||||||
import com.example.demo.order.OrderService;
|
|
||||||
import com.example.demo.product.ProductDto;
|
|
||||||
import com.example.demo.product.ProductService;
|
|
||||||
import org.springframework.stereotype.Controller;
|
|
||||||
import org.springframework.ui.Model;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
|
|
||||||
@Controller
|
|
||||||
@RequestMapping("/master")
|
|
||||||
public class MasterMvcController {
|
|
||||||
private final MasterService masterService;
|
|
||||||
private final OrderService orderService;
|
|
||||||
|
|
||||||
public MasterMvcController(MasterService masterService, OrderService orderService) {
|
|
||||||
this.masterService = masterService;
|
|
||||||
this.orderService = orderService;
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/login")
|
|
||||||
public String loginPage(Model model) {
|
|
||||||
if (masterService.getCurrentMasterId() != 0) {
|
|
||||||
return "redirect:/product";
|
|
||||||
}
|
|
||||||
model.addAttribute("user", new Master());
|
|
||||||
return "Login";
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping("/login")
|
|
||||||
public String login(@ModelAttribute Master user) {
|
|
||||||
var master = new MasterDto(masterService.findMaster(user.getEmail(), user.getPassword()));
|
|
||||||
masterService.setCurrentMasterId(master.getId());
|
|
||||||
return "redirect:/product/my_products";
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("")
|
|
||||||
public String getUserPage(Model model) {
|
|
||||||
if (masterService.getCurrentMasterId() != 0) {
|
|
||||||
model.addAttribute("user", masterService.findMaster(masterService.getCurrentMasterId()));
|
|
||||||
} else {
|
|
||||||
return "redirect:/master/register";
|
|
||||||
}
|
|
||||||
return "UserPage";
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping(value = "", params = "action=update")
|
|
||||||
public String updateUserPage(@ModelAttribute Master user) {
|
|
||||||
if (masterService.getCurrentMasterId() == 0) {
|
|
||||||
return "redirect:/master";
|
|
||||||
}
|
|
||||||
masterService.updateMaster(
|
|
||||||
masterService.getCurrentMasterId(),
|
|
||||||
user.getFirstName(),
|
|
||||||
user.getLastName(),
|
|
||||||
user.getEmail(),
|
|
||||||
user.getPassword());
|
|
||||||
return "redirect:/product";
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping(value = "", params = "action=log_out")
|
|
||||||
public String logOut() {
|
|
||||||
masterService.setCurrentMasterId(0L);
|
|
||||||
return "redirect:/product";
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/register")
|
|
||||||
public String registerPage(Model model) {
|
|
||||||
model.addAttribute("user", new Master());
|
|
||||||
return "UserPage";
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping(value = "/register", params = "action=register")
|
|
||||||
public String register(@ModelAttribute Master user) {
|
|
||||||
MasterDto master = new MasterDto(masterService.addMaster(
|
|
||||||
user.getFirstName(),
|
|
||||||
user.getLastName(),
|
|
||||||
user.getEmail(),
|
|
||||||
user.getPassword()));
|
|
||||||
masterService.setCurrentMasterId(master.getId());
|
|
||||||
orderService.addOrder(master.getId());
|
|
||||||
return "redirect:/product/my_products";
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,4 +7,6 @@ import java.util.Optional;
|
|||||||
|
|
||||||
public interface MasterRepository extends JpaRepository<Master, Long> {
|
public interface MasterRepository extends JpaRepository<Master, Long> {
|
||||||
Optional<Master> findByEmail(String email);
|
Optional<Master> findByEmail(String email);
|
||||||
|
|
||||||
|
Master findOneByEmailIgnoreCase(String login);
|
||||||
}
|
}
|
||||||
|
20
src/main/java/com/example/demo/master/MasterRole.java
Normal file
20
src/main/java/com/example/demo/master/MasterRole.java
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package com.example.demo.master;
|
||||||
|
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
|
||||||
|
public enum MasterRole implements GrantedAuthority {
|
||||||
|
ADMIN,
|
||||||
|
USER;
|
||||||
|
|
||||||
|
private static final String PREFIX = "ROLE_";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAuthority() {
|
||||||
|
return PREFIX + this.name();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class AsString {
|
||||||
|
public static final String ADMIN = PREFIX + "ADMIN";
|
||||||
|
public static final String USER = PREFIX + "USER";
|
||||||
|
}
|
||||||
|
}
|
@ -1,32 +1,51 @@
|
|||||||
package com.example.demo.master;
|
package com.example.demo.master;
|
||||||
|
|
||||||
|
import com.example.demo.configuration.jwt.JwtException;
|
||||||
|
import com.example.demo.configuration.jwt.JwtProvider;
|
||||||
import com.example.demo.order.Order;
|
import com.example.demo.order.Order;
|
||||||
import com.example.demo.order.OrderController;
|
import com.example.demo.order.OrderController;
|
||||||
import com.example.demo.order.OrderService;
|
import com.example.demo.order.OrderService;
|
||||||
|
import com.example.demo.util.validation.ValidationException;
|
||||||
import com.example.demo.util.validation.ValidatorUtil;
|
import com.example.demo.util.validation.ValidatorUtil;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class MasterService {
|
public class MasterService implements UserDetailsService {
|
||||||
private final MasterRepository masterRepository;
|
private final MasterRepository masterRepository;
|
||||||
|
|
||||||
private final ValidatorUtil validatorUtil;
|
private final ValidatorUtil validatorUtil;
|
||||||
|
private final PasswordEncoder passwordEncoder;
|
||||||
|
|
||||||
private Long currentMasterId = 0L;
|
private final JwtProvider jwtProvider;
|
||||||
|
public MasterService(MasterRepository masterRepository, ValidatorUtil validatorUtil,
|
||||||
public MasterService(MasterRepository masterRepository, ValidatorUtil validatorUtil) {
|
PasswordEncoder passwordEncoder, JwtProvider jwtProvider) {
|
||||||
this.masterRepository = masterRepository;
|
this.masterRepository = masterRepository;
|
||||||
this.validatorUtil = validatorUtil;
|
this.validatorUtil = validatorUtil;
|
||||||
|
this.passwordEncoder = passwordEncoder;
|
||||||
|
this.jwtProvider = jwtProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public Master addMaster(String firstName, String lastName, String email, String password, MasterRole role) {
|
||||||
|
final Master master = new Master(firstName, lastName, email, passwordEncoder.encode(password), role);
|
||||||
|
validatorUtil.validate(master);
|
||||||
|
|
||||||
|
return masterRepository.save(master);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public Master addMaster(String firstName, String lastName, String email, String password) {
|
public Master addMaster(String firstName, String lastName, String email, String password) {
|
||||||
final Master master = new Master(firstName, lastName, email, password);
|
final Master master = new Master(firstName, lastName, email, passwordEncoder.encode(password), MasterRole.USER);
|
||||||
validatorUtil.validate(master);
|
validatorUtil.validate(master);
|
||||||
return masterRepository.save(master);
|
return masterRepository.save(master);
|
||||||
}
|
}
|
||||||
@ -48,6 +67,11 @@ public class MasterService {
|
|||||||
return realMaster;
|
return realMaster;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public Master findMaster(String email) {
|
||||||
|
return masterRepository.findOneByEmailIgnoreCase(email);
|
||||||
|
}
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public List<Master> findAllMasters() {
|
public List<Master> findAllMasters() {
|
||||||
return masterRepository.findAll();
|
return masterRepository.findAll();
|
||||||
@ -76,14 +100,34 @@ public class MasterService {
|
|||||||
masterRepository.deleteAll();
|
masterRepository.deleteAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
public String loginAndGetToken(MasterDto userDto) {
|
||||||
public Long getCurrentMasterId (){
|
final Master master = findMaster(userDto.getEmail());
|
||||||
return currentMasterId;
|
if (master == null) {
|
||||||
|
throw new MasterNotFoundException(userDto.getEmail());
|
||||||
|
}
|
||||||
|
if (!passwordEncoder.matches(userDto.getPassword(), master.getPassword())) {
|
||||||
|
throw new ValidationException("Incorrect password");
|
||||||
|
}
|
||||||
|
return jwtProvider.generateToken(master.getEmail());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
public UserDetails loadUserByToken(String token) throws UsernameNotFoundException {
|
||||||
public void setCurrentMasterId(Long masterId) {
|
if (!jwtProvider.isTokenValid(token)) {
|
||||||
currentMasterId = masterId;
|
throw new JwtException("Bad token");
|
||||||
|
}
|
||||||
|
final String userLogin = jwtProvider.getLoginFromToken(token)
|
||||||
|
.orElseThrow(() -> new JwtException("Token is not contain Login"));
|
||||||
|
return loadUserByUsername(userLogin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||||
|
final Master userEntity = findMaster(username);
|
||||||
|
if (userEntity == null) {
|
||||||
|
throw new UsernameNotFoundException(username);
|
||||||
|
}
|
||||||
|
return new org.springframework.security.core.userdetails.User(
|
||||||
|
userEntity.getEmail(), userEntity.getPassword(), Collections.singleton(userEntity.getRole()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
35
src/main/java/com/example/demo/master/MasterSignupDto.java
Normal file
35
src/main/java/com/example/demo/master/MasterSignupDto.java
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package com.example.demo.master;
|
||||||
|
|
||||||
|
public class MasterSignupDto {
|
||||||
|
private String firstName;
|
||||||
|
private String lastName;
|
||||||
|
private String email;
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
|
||||||
|
public String getFirstName() {
|
||||||
|
return firstName;
|
||||||
|
}
|
||||||
|
public String getLastName() {
|
||||||
|
return lastName;
|
||||||
|
}
|
||||||
|
public String getEmail() {return email; }
|
||||||
|
|
||||||
|
public String getPassword() { return password; }
|
||||||
|
|
||||||
|
public void setFirstName(String firstName) {
|
||||||
|
this.firstName = firstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastName(String lastName) {
|
||||||
|
this.lastName = lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmail(String email) {
|
||||||
|
this.email = email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,15 @@
|
|||||||
package com.example.demo.order;
|
package com.example.demo.order;
|
||||||
|
|
||||||
import com.example.demo.WebConfiguration;
|
import com.example.demo.configuration.OpenAPI30Configuration;
|
||||||
|
import com.example.demo.configuration.WebConfiguration;
|
||||||
import com.example.demo.master.MasterService;
|
import com.example.demo.master.MasterService;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.security.Principal;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping(WebConfiguration.REST_API + "/order")
|
@RequestMapping(OpenAPI30Configuration.API_PREFIX + "/order")
|
||||||
public class OrderController {
|
public class OrderController {
|
||||||
private final OrderService orderService;
|
private final OrderService orderService;
|
||||||
private final MasterService masterService;
|
private final MasterService masterService;
|
||||||
@ -23,8 +25,8 @@ public class OrderController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/")
|
@DeleteMapping("/")
|
||||||
public void buyProducts() {
|
public void buyProducts(Principal principal) {
|
||||||
orderService.buyProducts(masterService.getCurrentMasterId());
|
orderService.buyProducts(masterService.findMaster(principal.getName()).getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/")
|
@GetMapping("/")
|
||||||
@ -38,13 +40,13 @@ public class OrderController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{product}")
|
@PostMapping("/{product}")
|
||||||
public void addProduct(@PathVariable("product") Long productId) {
|
public void addProduct(@PathVariable("product") Long productId, Principal principal) {
|
||||||
orderService.addProduct(masterService.getCurrentMasterId(), productId);
|
orderService.addProduct(masterService.findMaster(principal.getName()).getId(), productId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/{product}")
|
@DeleteMapping("/{product}")
|
||||||
public void deleteProduct(@PathVariable("product") Long productId) {
|
public void deleteProduct(@PathVariable("product") Long productId, Principal principal) {
|
||||||
orderService.deleteProduct(masterService.getCurrentMasterId(), productId);
|
orderService.deleteProduct(masterService.findMaster(principal.getName()).getId(), productId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/findOrders/{masterId}")
|
@GetMapping("/findOrders/{masterId}")
|
||||||
|
@ -1,66 +0,0 @@
|
|||||||
package com.example.demo.order;
|
|
||||||
|
|
||||||
import com.example.demo.master.MasterService;
|
|
||||||
import org.springframework.stereotype.Controller;
|
|
||||||
import org.springframework.ui.Model;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
|
|
||||||
@Controller
|
|
||||||
@RequestMapping("/order")
|
|
||||||
public class OrderMvcController {
|
|
||||||
private final MasterService masterService;
|
|
||||||
private final OrderService orderService;
|
|
||||||
|
|
||||||
public OrderMvcController(MasterService masterService, OrderService orderService) {
|
|
||||||
this.masterService = masterService;
|
|
||||||
this.orderService = orderService;
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("")
|
|
||||||
public String getOrder(Model model) {
|
|
||||||
if (masterService.getCurrentMasterId() == 0) {
|
|
||||||
return "redirect:/master/login";
|
|
||||||
}
|
|
||||||
model.addAttribute("user", masterService.findMaster(masterService.getCurrentMasterId()));
|
|
||||||
model.addAttribute("order", orderService.findOrder(masterService.getCurrentMasterId()));
|
|
||||||
AtomicInteger fullCost = new AtomicInteger();
|
|
||||||
orderService.findOrder(masterService.getCurrentMasterId()).getProducts().forEach(
|
|
||||||
item -> fullCost.addAndGet(item.getCost()));
|
|
||||||
model.addAttribute("fullCost", fullCost);
|
|
||||||
return "OrderPage";
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping(value = "", params = "action=delete")
|
|
||||||
public String deleteProduct(@RequestParam(value = "id", required = true) Long id) {
|
|
||||||
orderService.deleteProduct(masterService.getCurrentMasterId(), id);
|
|
||||||
return "redirect:/order";
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping(value = "", params = "action=buy")
|
|
||||||
public String buyProducts() {
|
|
||||||
orderService.buyProducts(masterService.getCurrentMasterId());
|
|
||||||
return "redirect:/product";
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/masters_order")
|
|
||||||
public String MastersOrders(Model model, @RequestParam(value = "master_id", defaultValue = "-1") String masterId) {
|
|
||||||
model.addAttribute("user", masterService.findMaster(masterService.getCurrentMasterId()));
|
|
||||||
model.addAttribute("masters", masterService.findAllMasters());
|
|
||||||
|
|
||||||
if (!Objects.equals(masterId, "-1")) {
|
|
||||||
model.addAttribute("orders", orderService.findMastersOrders(Long.valueOf(masterId)).
|
|
||||||
stream().map(OrderDto::new).toList());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
model.addAttribute("orders", new ArrayList<OrderDto>());
|
|
||||||
}
|
|
||||||
return "MastersOrders";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
package com.example.demo.product;
|
package com.example.demo.product;
|
||||||
|
|
||||||
import com.example.demo.WebConfiguration;
|
import com.example.demo.configuration.OpenAPI30Configuration;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import com.example.demo.configuration.WebConfiguration;
|
||||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PatchMapping;
|
import org.springframework.web.bind.annotation.PatchMapping;
|
||||||
@ -13,7 +13,7 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping(WebConfiguration.REST_API + "/product")
|
@RequestMapping(OpenAPI30Configuration.API_PREFIX + "/product")
|
||||||
public class ProductController {
|
public class ProductController {
|
||||||
private final ProductService productService;
|
private final ProductService productService;
|
||||||
|
|
||||||
|
@ -1,102 +0,0 @@
|
|||||||
package com.example.demo.product;
|
|
||||||
|
|
||||||
import com.example.demo.master.Master;
|
|
||||||
import com.example.demo.master.MasterService;
|
|
||||||
import com.example.demo.order.OrderService;
|
|
||||||
import org.springframework.stereotype.Controller;
|
|
||||||
import org.springframework.ui.Model;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
|
|
||||||
|
|
||||||
@Controller
|
|
||||||
@RequestMapping("/product")
|
|
||||||
public class ProductMvcController {
|
|
||||||
private final ProductService productService;
|
|
||||||
private final MasterService masterService;
|
|
||||||
private final OrderService orderService;
|
|
||||||
public ProductMvcController(MasterService masterService,
|
|
||||||
ProductService productService,
|
|
||||||
OrderService orderService) {
|
|
||||||
this.masterService = masterService;
|
|
||||||
this.productService = productService;
|
|
||||||
this.orderService = orderService;
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("")
|
|
||||||
public String getProducts(Model model) {
|
|
||||||
if (masterService.getCurrentMasterId() != 0) {
|
|
||||||
Master user = masterService.findMaster(masterService.getCurrentMasterId());
|
|
||||||
model.addAttribute("user", user);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
model.addAttribute("user", new Master());
|
|
||||||
}
|
|
||||||
model.addAttribute("products", productService.findAllProducts()
|
|
||||||
.stream().map(ProductDto::new).toList());
|
|
||||||
return "Products";
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping("")
|
|
||||||
public String addProductToOrder(@RequestParam(value = "id", required = true) Long id) {
|
|
||||||
if (masterService.getCurrentMasterId() == 0) {
|
|
||||||
return "redirect:/master/login";
|
|
||||||
}
|
|
||||||
orderService.addProduct(masterService.getCurrentMasterId(), id);
|
|
||||||
return "redirect:/product";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@GetMapping("/my_products")
|
|
||||||
public String getMasterProduct(Model model) {
|
|
||||||
if (masterService.getCurrentMasterId() == 0) {
|
|
||||||
return "redirect:/product";
|
|
||||||
}
|
|
||||||
model.addAttribute("user",
|
|
||||||
masterService.findMaster(masterService.getCurrentMasterId()));
|
|
||||||
model.addAttribute("products",
|
|
||||||
productService.findProducts(masterService.getCurrentMasterId()).stream().map(ProductDto::new).toList());
|
|
||||||
return "UserProducts";
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/create_product")
|
|
||||||
public String createProductPage(Model model) {
|
|
||||||
if (masterService.getCurrentMasterId() == 0) {
|
|
||||||
return "redirect:/product";
|
|
||||||
}
|
|
||||||
model.addAttribute("user", masterService.findMaster(masterService.getCurrentMasterId()));
|
|
||||||
model.addAttribute("product", new Product());
|
|
||||||
model.addAttribute("buttonText", "Create");
|
|
||||||
return "ProductCreate";
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping("/create_product")
|
|
||||||
public String createProduct(@ModelAttribute Product product) {
|
|
||||||
productService.addProduct(
|
|
||||||
product.getName(),
|
|
||||||
product.getCost(),
|
|
||||||
masterService.getCurrentMasterId()
|
|
||||||
);
|
|
||||||
return "redirect:/product/my_products";
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/update_product/{id}")
|
|
||||||
public String updateProductPage(Model model, @PathVariable("id") Long id) {
|
|
||||||
if (masterService.getCurrentMasterId() == 0) {
|
|
||||||
return "redirect:/product";
|
|
||||||
}
|
|
||||||
model.addAttribute("user", masterService.findMaster(masterService.getCurrentMasterId()));
|
|
||||||
model.addAttribute("product", productService.findProduct(id));
|
|
||||||
model.addAttribute("buttonText", "Update");
|
|
||||||
return "ProductCreate";
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping("/update_product/{id}")
|
|
||||||
public String createProduct(@ModelAttribute Product product, @PathVariable("id") Long id) {
|
|
||||||
productService.updateProduct(
|
|
||||||
id,
|
|
||||||
product.getName(),
|
|
||||||
product.getCost()
|
|
||||||
);
|
|
||||||
return "redirect:/product/my_products";
|
|
||||||
}
|
|
||||||
}
|
|
@ -6,4 +6,8 @@ public class ValidationException extends RuntimeException {
|
|||||||
public ValidationException(Set<String> errors) {
|
public ValidationException(Set<String> errors) {
|
||||||
super(String.join("\n", errors));
|
super(String.join("\n", errors));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public <T> ValidationException(String error) {
|
||||||
|
super(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Title</title>
|
|
||||||
<link rel="stylesheet" href="/style.css"/>
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css"
|
|
||||||
rel="stylesheet" integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ"
|
|
||||||
crossorigin="anonymous">
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.7/dist/umd/popper.min.js"
|
|
||||||
integrity="sha384-zYPOMqeu1DAVkHiLqWBUTcbYfZ8osu1Nd6Z89ify25QV9guujx43ITvfi12/QExE"
|
|
||||||
crossorigin="anonymous"></script>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.min.js"
|
|
||||||
integrity="sha384-Y4oOpwW3duJdCWv5ly8SCFYWqFDsfob/3GkgExXKV4idmbt98QcxXYs9UoXAB7BZ"
|
|
||||||
crossorigin="anonymous"></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<header class="logo d-flex flex-wrap align-items-center justify-content-center
|
|
||||||
justify-content-md-between py-3 mb-4 border-bottom" th:fragment="header">
|
|
||||||
<a href="/" class="d-flex align-items-center col-md-3 mb-2 mb-md-0 text-dark text-decoration-none">
|
|
||||||
<svg class="bi me-2" width="40" height="32" role="img" aria-label="Bootstrap"><use xlink:href="#bootstrap"></use></svg>
|
|
||||||
</a>
|
|
||||||
<ul class="nav col-12 col-md-auto mb-2 justify-content-center mb-md-0">
|
|
||||||
<li th:if="${user.firstName != null}"><a href="/product/my_products" class="nav-link px-2 link-dark">My Products</a></li>
|
|
||||||
<li><a href="/product" class="nav-link px-2 link-dark">Products</a></li>
|
|
||||||
<li th:if="${user.firstName != null}"><a href="/order/masters_order" class="nav-link px-2 link-dark">Orders</a></li>
|
|
||||||
</ul>
|
|
||||||
<div th:if="${user.firstName != null}" class="col-md-3 text-end">
|
|
||||||
<a href="/order" class="btn btn-outline-primary me-2">Shop List</a>
|
|
||||||
<a href="/master" class="nav-link px-2 link-dark" th:text="${user.firstName + ' ' + user.lastName}">User Name</a>
|
|
||||||
</div>
|
|
||||||
<div th:unless="${user.firstName != null}" class="col-md-3 text-end">
|
|
||||||
<a href="/master/login" class="btn btn-outline-primary me-2">Login</a>
|
|
||||||
<a href="/master" class="btn btn-primary">Register</a>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,38 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Login</title>
|
|
||||||
<link rel="stylesheet" href="/style.css"/>
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css"
|
|
||||||
rel="stylesheet" integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ"
|
|
||||||
crossorigin="anonymous">
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.7/dist/umd/popper.min.js"
|
|
||||||
integrity="sha384-zYPOMqeu1DAVkHiLqWBUTcbYfZ8osu1Nd6Z89ify25QV9guujx43ITvfi12/QExE"
|
|
||||||
crossorigin="anonymous"></script>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.min.js"
|
|
||||||
integrity="sha384-Y4oOpwW3duJdCWv5ly8SCFYWqFDsfob/3GkgExXKV4idmbt98QcxXYs9UoXAB7BZ"
|
|
||||||
crossorigin="anonymous"></script>
|
|
||||||
</head>
|
|
||||||
<body style="background: #f54d9a">
|
|
||||||
<header th:insert="~{Header :: header}"></header>
|
|
||||||
<form method="post" th:object="${user}">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row gy-5 p-2">
|
|
||||||
<input type="email" name="email" class="form-control"
|
|
||||||
id="email" placeholder="Email" th:field="*{email}">
|
|
||||||
</div>
|
|
||||||
<div class="row gy-5 p-2">
|
|
||||||
<input type="password" name="password" class="form-control"
|
|
||||||
id="password" placeholder="Password" th:field="*{password}">
|
|
||||||
</div>
|
|
||||||
<div class="row gy-5">
|
|
||||||
<button type="submit" class="btn btn-primary">Sing In</button>
|
|
||||||
</div>
|
|
||||||
<div class="row gy-5 ">
|
|
||||||
<a href="/master/register" class="btn btn-primary">Register</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,51 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Login</title>
|
|
||||||
<link rel="stylesheet" href="/style.css"/>
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css"
|
|
||||||
rel="stylesheet" integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ"
|
|
||||||
crossorigin="anonymous">
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.7/dist/umd/popper.min.js"
|
|
||||||
integrity="sha384-zYPOMqeu1DAVkHiLqWBUTcbYfZ8osu1Nd6Z89ify25QV9guujx43ITvfi12/QExE"
|
|
||||||
crossorigin="anonymous"></script>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.min.js"
|
|
||||||
integrity="sha384-Y4oOpwW3duJdCWv5ly8SCFYWqFDsfob/3GkgExXKV4idmbt98QcxXYs9UoXAB7BZ"
|
|
||||||
crossorigin="anonymous"></script>
|
|
||||||
</head>
|
|
||||||
<body style="background: #f54d9a">
|
|
||||||
<header th:insert="~{Header :: header}"></header>
|
|
||||||
<div>
|
|
||||||
<h1 class="text-center">Masters</h1>
|
|
||||||
|
|
||||||
<div class="container">
|
|
||||||
<div class="row gy-5 p-2">
|
|
||||||
<form th:action="@{/order/masters_order}" method="get">
|
|
||||||
<select th:each="master : ${masters}" th:name="master_id" class="form-select" aria-label="Default select example">
|
|
||||||
<option th:id="master_id" th:value="${master.id}" >
|
|
||||||
<span th:text="${master.firstName + ' ' + master.lastName }"></span>
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
<button class="btn btn-primary w-100">Check</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<table class="table table-dark">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th scope="col">#</th>
|
|
||||||
<th scope="col">Cost</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr th:each="order : ${orders}">
|
|
||||||
<th scope="row" th:text="${order.id}"></th>
|
|
||||||
<td th:text="${order.cost}"></td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,48 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Login</title>
|
|
||||||
<link rel="stylesheet" href="/style.css"/>
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css"
|
|
||||||
rel="stylesheet" integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ"
|
|
||||||
crossorigin="anonymous">
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.7/dist/umd/popper.min.js"
|
|
||||||
integrity="sha384-zYPOMqeu1DAVkHiLqWBUTcbYfZ8osu1Nd6Z89ify25QV9guujx43ITvfi12/QExE"
|
|
||||||
crossorigin="anonymous"></script>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.min.js"
|
|
||||||
integrity="sha384-Y4oOpwW3duJdCWv5ly8SCFYWqFDsfob/3GkgExXKV4idmbt98QcxXYs9UoXAB7BZ"
|
|
||||||
crossorigin="anonymous"></script>
|
|
||||||
</head>
|
|
||||||
<body style="background: #f54d9a">
|
|
||||||
<header th:insert="~{Header :: header}"></header>
|
|
||||||
<div>
|
|
||||||
<h1 class="text-center">Order</h1>
|
|
||||||
<form method="post" th:object="${order}">
|
|
||||||
<div class="container">
|
|
||||||
<div th:each="product : ${order.products}" class="row product-div p-2 m-2">
|
|
||||||
<input type="hidden" name="id_item" th:name="id" th:value="${product.id}">
|
|
||||||
<div class="col">
|
|
||||||
<h2 th:text="${product.name}"></h2>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<h3 th:text="${product.cost} + '$'"></h3>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<button name="action" value="delete" class="btn btn-primary w-100 h-100 text-lg-center">Delete</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col">
|
|
||||||
<h2 th:text="${fullCost} + '$'"></h2>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<h3><button name="action" value="buy" class="btn btn-primary w-100">Buy</button>
|
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,27 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Login</title>
|
|
||||||
<link rel="stylesheet" href="/style.css"/>
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css"
|
|
||||||
rel="stylesheet" integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ"
|
|
||||||
crossorigin="anonymous">
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.7/dist/umd/popper.min.js"
|
|
||||||
integrity="sha384-zYPOMqeu1DAVkHiLqWBUTcbYfZ8osu1Nd6Z89ify25QV9guujx43ITvfi12/QExE"
|
|
||||||
crossorigin="anonymous"></script>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.min.js"
|
|
||||||
integrity="sha384-Y4oOpwW3duJdCWv5ly8SCFYWqFDsfob/3GkgExXKV4idmbt98QcxXYs9UoXAB7BZ"
|
|
||||||
crossorigin="anonymous"></script>
|
|
||||||
</head>
|
|
||||||
<body style="background: #f54d9a">
|
|
||||||
<header th:insert="~{Header :: header}"></header>
|
|
||||||
<div>
|
|
||||||
<form method="post" th:object="${product}">
|
|
||||||
<input type="text" th:field="*{name}" th:value="${product.getName()}" name="name" class="form-control" id="name">
|
|
||||||
<input type="number" th:field="*{cost}" th:value="${product.getCost()}" name="cost" class="form-control" id="cost">
|
|
||||||
<button type="submit" class="btn btn-primary" th:text="${buttonText}">Add</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,38 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Title</title>
|
|
||||||
<link rel="stylesheet" href="/style.css"/>
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css"
|
|
||||||
rel="stylesheet" integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ"
|
|
||||||
crossorigin="anonymous">
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.7/dist/umd/popper.min.js"
|
|
||||||
integrity="sha384-zYPOMqeu1DAVkHiLqWBUTcbYfZ8osu1Nd6Z89ify25QV9guujx43ITvfi12/QExE"
|
|
||||||
crossorigin="anonymous"></script>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.min.js"
|
|
||||||
integrity="sha384-Y4oOpwW3duJdCWv5ly8SCFYWqFDsfob/3GkgExXKV4idmbt98QcxXYs9UoXAB7BZ"
|
|
||||||
crossorigin="anonymous"></script>
|
|
||||||
</head>
|
|
||||||
<body style="background: #f54d9a">
|
|
||||||
<header th:insert="~{Header :: header}"></header>
|
|
||||||
|
|
||||||
<div class="container">
|
|
||||||
<div th:each="item : ${products}" class="row product-div p-2 m-2">
|
|
||||||
<form method="post" th:object="${item}" >
|
|
||||||
<input type="hidden" th:name="id" th:value="${item.getId()}">
|
|
||||||
<div class="col">
|
|
||||||
<h2 th:text="${item.getName()}" th:name="*{name}"></h2>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<h3 th:text="${item.getCost()} + '$'" th:name="*{cost}"></h3>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<button type="submit" class="btn btn-primary w-100">Buy</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,55 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Title</title>
|
|
||||||
<link rel="stylesheet" href="/style.css"/>
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css"
|
|
||||||
rel="stylesheet" integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ"
|
|
||||||
crossorigin="anonymous">
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.7/dist/umd/popper.min.js"
|
|
||||||
integrity="sha384-zYPOMqeu1DAVkHiLqWBUTcbYfZ8osu1Nd6Z89ify25QV9guujx43ITvfi12/QExE"
|
|
||||||
crossorigin="anonymous"></script>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.min.js"
|
|
||||||
integrity="sha384-Y4oOpwW3duJdCWv5ly8SCFYWqFDsfob/3GkgExXKV4idmbt98QcxXYs9UoXAB7BZ"
|
|
||||||
crossorigin="anonymous"></script>
|
|
||||||
</head>
|
|
||||||
<body style="background: #f54d9a">
|
|
||||||
<header th:insert="~{Header :: header}"></header>
|
|
||||||
<form method="post" th:object="${user}">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row gy-5 p-2">
|
|
||||||
<input type="text" name="name" class="form-control"
|
|
||||||
id="name" placeholder="Name" th:value="${user.firstName}" th:field="*{firstName}">
|
|
||||||
</div>
|
|
||||||
<div class="row gy-5 p-2">
|
|
||||||
<input type="text" name="surname" class="form-control"
|
|
||||||
id="surname" placeholder="Surname" th:value="${user.lastName}" th:field="*{lastName}">
|
|
||||||
</div>
|
|
||||||
<div class="row gy-5 p-2">
|
|
||||||
<input type="email" name="email" class="form-control"
|
|
||||||
id="email" placeholder="Email" th:value="${user.email}" th:field="*{email}">
|
|
||||||
</div>
|
|
||||||
<div class="row gy-5 p-2">
|
|
||||||
<input type="password" name="password" class="form-control"
|
|
||||||
id="password" placeholder="Password" th:value="${user.password}" th:field="*{password}">
|
|
||||||
</div>
|
|
||||||
<div th:if='${user.firstName != null}' class="row align-items-start">
|
|
||||||
<div class="col">
|
|
||||||
<button name="action" class="btn btn-primary w-100" value="update">Update</button>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<button name="action" th:onclick="/master/log_out"
|
|
||||||
class="btn btn-primary w-100" value="log_out">Log Out</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div th:unless='${user.firstName != null}' class="row gy-5">
|
|
||||||
<button name="action" value="register" class="btn btn-primary w-100">Register</button>
|
|
||||||
</div>
|
|
||||||
<div th:unless='${user.firstName != null}' class="row align-items-start">
|
|
||||||
<a href="/sing_in" class="btn btn-primary">Sing In</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,37 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Title</title>
|
|
||||||
<link rel="stylesheet" href="/style.css"/>
|
|
||||||
<a></a>
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css"
|
|
||||||
rel="stylesheet" integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ"
|
|
||||||
crossorigin="anonymous">
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.7/dist/umd/popper.min.js"
|
|
||||||
integrity="sha384-zYPOMqeu1DAVkHiLqWBUTcbYfZ8osu1Nd6Z89ify25QV9guujx43ITvfi12/QExE"
|
|
||||||
crossorigin="anonymous"></script>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.min.js"
|
|
||||||
integrity="sha384-Y4oOpwW3duJdCWv5ly8SCFYWqFDsfob/3GkgExXKV4idmbt98QcxXYs9UoXAB7BZ"
|
|
||||||
crossorigin="anonymous"></script>
|
|
||||||
</head>
|
|
||||||
<body style="background: #f54d9a">
|
|
||||||
<header th:insert="~{Header :: header}"></header>
|
|
||||||
<div class="container">
|
|
||||||
<div class="row align-content-center">
|
|
||||||
<a href="/product/create_product" class="btn btn-primary w-100 h-100 text-lg-center">Create</a>
|
|
||||||
</div>
|
|
||||||
<div th:each="item : ${products}" class="row product-div p-2 m-2">
|
|
||||||
<div class="col">
|
|
||||||
<h2 th:text="${item.name}"></h2>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<h3 th:text="${item.cost} + '$'"></h3>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<a th:href="'/product/update_product/' + ${{item.id}}" class="btn btn-primary w-100 h-100 text-lg-center">Edit</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
Loading…
x
Reference in New Issue
Block a user