login/signup commit
This commit is contained in:
parent
8386db6502
commit
aecd4fd77a
103
pom.xml
Normal file
103
pom.xml
Normal file
@ -0,0 +1,103 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.alterdekim.game</groupId>
|
||||
<artifactId>NoDice</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.0.0</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
<version>1.2.17</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.thymeleaf.extras</groupId>
|
||||
<artifactId>thymeleaf-extras-springsecurity6</artifactId>
|
||||
<!-- Temporary explicit version to fix Thymeleaf bug -->
|
||||
<version>3.1.1.RELEASE</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>8.0.33</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
<version>1.18.28</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
@ -1,2 +1,24 @@
|
||||
package com.alterdekim.game;public class Application {
|
||||
}
|
||||
package com.alterdekim.game;
|
||||
|
||||
import com.alterdekim.game.storage.StorageService;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableScheduling
|
||||
public class Application {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Application.class, args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
CommandLineRunner init(StorageService storageService) {
|
||||
return (args) -> {
|
||||
storageService.init();
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
package com.alterdekim.game.controller;
|
||||
|
||||
import com.alterdekim.game.dto.UserDTO;
|
||||
import com.alterdekim.game.entities.Invite;
|
||||
import com.alterdekim.game.entities.User;
|
||||
import com.alterdekim.game.service.InviteService;
|
||||
import com.alterdekim.game.service.UserService;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
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;
|
||||
|
||||
@Slf4j
|
||||
@Controller
|
||||
public class AuthController {
|
||||
|
||||
private final String base_title = " | Nosedive";
|
||||
|
||||
private final UserService userService;
|
||||
private final InviteService inviteService;
|
||||
|
||||
public AuthController(UserService userService, InviteService inviteService) {
|
||||
this.inviteService = inviteService;
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
@GetMapping("/game")
|
||||
public String gamePage(Model model) {
|
||||
return "game";
|
||||
}
|
||||
|
||||
@GetMapping("/login")
|
||||
public String loginPage(Model model) {
|
||||
model.addAttribute("title", "Login" + base_title);
|
||||
return "login";
|
||||
}
|
||||
|
||||
@GetMapping("/access-denied")
|
||||
public String accessDenied(Model model) {
|
||||
model.addAttribute("title", "Access denied");
|
||||
return "access-denied";
|
||||
}
|
||||
|
||||
@GetMapping("/signup")
|
||||
public String showRegistrationForm(Model model) {
|
||||
UserDTO userDto = new UserDTO();
|
||||
model.addAttribute("user", userDto);
|
||||
return "signup";
|
||||
}
|
||||
|
||||
@PostMapping("/signup")
|
||||
public String registration(
|
||||
@ModelAttribute("user") @Valid UserDTO userDto,
|
||||
BindingResult result,
|
||||
Model model) {
|
||||
User existingUser = userService.findByUsername(userDto.getUsername());
|
||||
Invite existingInvite = inviteService.findById(1);
|
||||
|
||||
if(existingUser != null && existingUser.getUsername() != null && !existingUser.getUsername().isEmpty() ){
|
||||
result.rejectValue("username", null,
|
||||
"There is already an account registered with the same username");
|
||||
return "redirect:/signup?error=1";
|
||||
}
|
||||
|
||||
if(!existingInvite.getInvite_code().equals(userDto.getInvite_code())) {
|
||||
result.rejectValue("invite_code", null, "Incorrect invite code.");
|
||||
return "redirect:/signup?error=1";
|
||||
}
|
||||
|
||||
if(result.hasErrors()) {
|
||||
model.addAttribute("user", new UserDTO());
|
||||
return "redirect:/signup?error=1";
|
||||
}
|
||||
|
||||
userService.saveUser(userDto);
|
||||
return "redirect:/";
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,46 @@
|
||||
package com.alterdekim.game.controller;
|
||||
|
||||
import com.alterdekim.game.storage.StorageFileNotFoundException;
|
||||
import com.alterdekim.game.storage.StorageProperties;
|
||||
import com.alterdekim.game.storage.StorageService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
@Controller
|
||||
@EnableConfigurationProperties(StorageProperties.class)
|
||||
public class FileServerController {
|
||||
|
||||
private final StorageService storageService;
|
||||
|
||||
@Autowired
|
||||
public FileServerController(StorageService storageService) {
|
||||
this.storageService = storageService;
|
||||
}
|
||||
|
||||
@GetMapping("/static/{assetspath}/{filename:.+}")
|
||||
@ResponseBody
|
||||
public ResponseEntity<Resource> serveFile(@PathVariable String filename, @PathVariable String assetspath) {
|
||||
Resource file = storageService.loadAsResource("static/" + assetspath + "/" + filename);
|
||||
if( assetspath.equals("images") ) {
|
||||
return ResponseEntity.ok().contentType(MediaType.IMAGE_PNG).header(HttpHeaders.CONTENT_DISPOSITION,
|
||||
"attachment; filename=\"" + file.getFilename() + "\"").body(file);
|
||||
}
|
||||
return ResponseEntity.ok().contentType(new MediaType("text", assetspath)).header(HttpHeaders.CONTENT_DISPOSITION,
|
||||
"attachment; filename=\"" + file.getFilename() + "\"").body(file);
|
||||
}
|
||||
|
||||
@ExceptionHandler(StorageFileNotFoundException.class)
|
||||
public ResponseEntity<?> handleStorageFileNotFound(StorageFileNotFoundException exc) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
}
|
||||
|
24
src/main/java/com/alterdekim/game/dto/UserDTO.java
Normal file
24
src/main/java/com/alterdekim/game/dto/UserDTO.java
Normal file
@ -0,0 +1,24 @@
|
||||
package com.alterdekim.game.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class UserDTO {
|
||||
private Long id;
|
||||
@NotEmpty(message = "Username should not be empty")
|
||||
private String username;
|
||||
@NotEmpty(message = "Password should not be empty")
|
||||
private String password;
|
||||
@NotEmpty(message = "Invite code should not be empty")
|
||||
private String invite_code;
|
||||
|
||||
private String lang;
|
||||
}
|
||||
|
23
src/main/java/com/alterdekim/game/entities/Invite.java
Normal file
23
src/main/java/com/alterdekim/game/entities/Invite.java
Normal file
@ -0,0 +1,23 @@
|
||||
package com.alterdekim.game.entities;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Entity
|
||||
@Table(name = "invite_codes")
|
||||
public class Invite {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable=false)
|
||||
private String invite_code;
|
||||
}
|
||||
|
29
src/main/java/com/alterdekim/game/entities/Role.java
Normal file
29
src/main/java/com/alterdekim/game/entities/Role.java
Normal file
@ -0,0 +1,29 @@
|
||||
package com.alterdekim.game.entities;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Entity
|
||||
@Table(name="roles")
|
||||
public class Role
|
||||
{
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable=false, unique=true)
|
||||
private String name;
|
||||
|
||||
@ManyToMany(mappedBy="roles")
|
||||
private List<User> users;
|
||||
}
|
||||
|
38
src/main/java/com/alterdekim/game/entities/User.java
Normal file
38
src/main/java/com/alterdekim/game/entities/User.java
Normal file
@ -0,0 +1,38 @@
|
||||
package com.alterdekim.game.entities;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Entity
|
||||
@Table(name="users")
|
||||
public class User {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable=false, unique=true)
|
||||
private String username;
|
||||
|
||||
@Column(nullable=false)
|
||||
private String password;
|
||||
|
||||
@ManyToMany(fetch = FetchType.EAGER, cascade=CascadeType.ALL)
|
||||
@JoinTable(
|
||||
name="users_roles",
|
||||
joinColumns={@JoinColumn(name="USER_ID", referencedColumnName="ID")},
|
||||
inverseJoinColumns={@JoinColumn(name="ROLE_ID", referencedColumnName="ID")})
|
||||
private List<Role> roles = new ArrayList<>();
|
||||
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package com.alterdekim.game.handler;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
|
||||
@Override
|
||||
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
|
||||
response.sendRedirect("/access-denied");
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,11 @@
|
||||
package com.alterdekim.game.repository;
|
||||
|
||||
import com.alterdekim.game.entities.Invite;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface InviteRepository extends JpaRepository<Invite, Integer> {
|
||||
Optional<Invite> findById(Integer id);
|
||||
}
|
||||
|
@ -0,0 +1,13 @@
|
||||
package com.alterdekim.game.repository;
|
||||
|
||||
import com.alterdekim.game.entities.Role;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface RoleRepository extends JpaRepository<Role, Long> {
|
||||
Role findByName(String name);
|
||||
|
||||
List<Role> findAll();
|
||||
}
|
||||
|
@ -0,0 +1,10 @@
|
||||
package com.alterdekim.game.repository;
|
||||
|
||||
import com.alterdekim.game.entities.User;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface UserRepository extends JpaRepository<User, Integer> {
|
||||
User findByUsername(String username);
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
package com.alterdekim.game.security;
|
||||
|
||||
import com.alterdekim.game.handler.CustomAccessDeniedHandler;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SpringSecurity {
|
||||
|
||||
@Autowired
|
||||
private UserDetailsService userDetailsService;
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
HttpSessionRequestCache requestCache = new HttpSessionRequestCache();
|
||||
requestCache.setMatchingRequestParameterName(null);
|
||||
http.csrf().disable()
|
||||
.authorizeHttpRequests((authorize) ->
|
||||
authorize
|
||||
.requestMatchers("/game").permitAll()
|
||||
.requestMatchers("/games").permitAll()
|
||||
.requestMatchers("/static/**").permitAll()
|
||||
.requestMatchers("/access-denied").permitAll()
|
||||
.requestMatchers("/signup").permitAll()
|
||||
.requestMatchers("/favicon.ico").permitAll()
|
||||
.requestMatchers("/signup/**").permitAll()
|
||||
.requestMatchers("/").permitAll()
|
||||
).formLogin(
|
||||
form -> form
|
||||
.loginPage("/login")
|
||||
.loginProcessingUrl("/login")
|
||||
.failureForwardUrl("/")
|
||||
.defaultSuccessUrl("/game")
|
||||
.permitAll()
|
||||
)
|
||||
.logout(
|
||||
logout -> logout
|
||||
.logoutUrl("/logout")
|
||||
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
|
||||
.permitAll()
|
||||
)
|
||||
.requestCache((cache) -> cache
|
||||
.requestCache(requestCache)
|
||||
)
|
||||
.exceptionHandling((exc) -> exc
|
||||
.accessDeniedHandler(accessDeniedHandler())
|
||||
.accessDeniedPage("/access-denied"));
|
||||
return http.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AccessDeniedHandler accessDeniedHandler() {
|
||||
return new CustomAccessDeniedHandler();
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public static PasswordEncoder passwordEncoder(){
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
package com.alterdekim.game.service;
|
||||
|
||||
import com.alterdekim.game.entities.Invite;
|
||||
|
||||
public interface InviteService {
|
||||
Invite findById(Integer id);
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package com.alterdekim.game.service;
|
||||
|
||||
import com.alterdekim.game.entities.Invite;
|
||||
import com.alterdekim.game.repository.InviteRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class InviteServiceImpl implements InviteService {
|
||||
|
||||
private final InviteRepository repository;
|
||||
|
||||
public InviteServiceImpl(InviteRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Invite findById(Integer id) {
|
||||
return repository.findById(id).orElse(null);
|
||||
}
|
||||
}
|
15
src/main/java/com/alterdekim/game/service/UserService.java
Normal file
15
src/main/java/com/alterdekim/game/service/UserService.java
Normal file
@ -0,0 +1,15 @@
|
||||
package com.alterdekim.game.service;
|
||||
|
||||
import com.alterdekim.game.dto.UserDTO;
|
||||
import com.alterdekim.game.entities.User;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface UserService {
|
||||
void saveUser(UserDTO userDto);
|
||||
|
||||
User findByUsername(String usernane);
|
||||
|
||||
List<UserDTO> findAllUsers();
|
||||
}
|
||||
|
@ -0,0 +1,68 @@
|
||||
package com.alterdekim.game.service;
|
||||
|
||||
import com.alterdekim.game.dto.UserDTO;
|
||||
import com.alterdekim.game.entities.Role;
|
||||
import com.alterdekim.game.entities.User;
|
||||
import com.alterdekim.game.repository.RoleRepository;
|
||||
import com.alterdekim.game.repository.UserRepository;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class UserServiceImpl implements UserService {
|
||||
|
||||
private final UserRepository userRepository;
|
||||
private final RoleRepository roleRepository;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
|
||||
public UserServiceImpl(UserRepository userRepository,
|
||||
RoleRepository roleRepository,
|
||||
PasswordEncoder passwordEncoder) {
|
||||
this.userRepository = userRepository;
|
||||
this.roleRepository = roleRepository;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveUser(UserDTO userDto) {
|
||||
User user = new User();
|
||||
user.setUsername(userDto.getUsername());
|
||||
|
||||
user.setPassword(passwordEncoder.encode(userDto.getPassword()));
|
||||
Role role = roleRepository.findByName("ROLE_ADMIN");
|
||||
if(role == null){
|
||||
role = checkRoleExist();
|
||||
}
|
||||
user.setRoles(Collections.singletonList(role));
|
||||
userRepository.save(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public User findByUsername(String username) {
|
||||
return userRepository.findByUsername(username);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserDTO> findAllUsers() {
|
||||
List<User> users = userRepository.findAll();
|
||||
return users.stream().map(this::convertEntityToDto)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private UserDTO convertEntityToDto(User user){
|
||||
UserDTO userDto = new UserDTO();
|
||||
userDto.setUsername(user.getUsername());
|
||||
return userDto;
|
||||
}
|
||||
|
||||
private Role checkRoleExist() {
|
||||
Role role = new Role();
|
||||
role.setName("ROLE_ADMIN");
|
||||
return roleRepository.save(role);
|
||||
}
|
||||
}
|
||||
|
@ -1,2 +1,106 @@
|
||||
package com.alterdekim.game.storage;public class FileSystemStorageService {
|
||||
package com.alterdekim.game.storage;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.FileSystemUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@Service
|
||||
public class FileSystemStorageService implements StorageService {
|
||||
|
||||
private final Path rootLocation;
|
||||
|
||||
@Autowired
|
||||
public FileSystemStorageService(StorageProperties properties) {
|
||||
|
||||
if(properties.getLocation().trim().isEmpty()){
|
||||
throw new StorageException("File upload location can not be Empty.");
|
||||
}
|
||||
|
||||
this.rootLocation = Paths.get(new ClassPathResource(properties.getLocation()).getPath());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void store(MultipartFile file) {
|
||||
try {
|
||||
if (file.isEmpty()) {
|
||||
throw new StorageException("Failed to store empty file.");
|
||||
}
|
||||
Path destinationFile = this.rootLocation.resolve(
|
||||
Paths.get(file.getOriginalFilename()))
|
||||
.normalize().toAbsolutePath();
|
||||
if (!destinationFile.getParent().equals(this.rootLocation.toAbsolutePath())) {
|
||||
throw new StorageException(
|
||||
"Cannot store file outside current directory.");
|
||||
}
|
||||
try (InputStream inputStream = file.getInputStream()) {
|
||||
Files.copy(inputStream, destinationFile,
|
||||
StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new StorageException("Failed to store file.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Path> loadAll() {
|
||||
try {
|
||||
return Files.walk(this.rootLocation, 1)
|
||||
.filter(path -> !path.equals(this.rootLocation))
|
||||
.map(this.rootLocation::relativize);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new StorageException("Failed to read stored files", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path load(String filename) {
|
||||
return rootLocation.resolve(filename);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource loadAsResource(String filename) {
|
||||
try {
|
||||
Resource resource = new ClassPathResource(filename);
|
||||
if (resource.exists() || resource.isReadable()) {
|
||||
return resource;
|
||||
}
|
||||
else {
|
||||
throw new StorageFileNotFoundException(
|
||||
"Could not read file: " + filename);
|
||||
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new StorageFileNotFoundException("Could not read file: " + filename, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAll() {
|
||||
FileSystemUtils.deleteRecursively(rootLocation.toFile());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
try {
|
||||
Files.createDirectories(rootLocation);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new StorageException("Could not initialize storage", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,2 +1,12 @@
|
||||
package com.alterdekim.game.storage;public class StorageException {
|
||||
package com.alterdekim.game.storage;
|
||||
|
||||
public class StorageException extends RuntimeException {
|
||||
|
||||
public StorageException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public StorageException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
|
@ -1,2 +1,12 @@
|
||||
package com.alterdekim.game.storage;public class StorageFileNotFoundException {
|
||||
}
|
||||
package com.alterdekim.game.storage;
|
||||
|
||||
public class StorageFileNotFoundException extends StorageException {
|
||||
|
||||
public StorageFileNotFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public StorageFileNotFoundException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
@ -1,2 +1,12 @@
|
||||
package com.alterdekim.game.storage;public class StorageProperties {
|
||||
package com.alterdekim.game.storage;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
@ConfigurationProperties("storage")
|
||||
public class StorageProperties {
|
||||
private String location = "static";
|
||||
}
|
||||
|
@ -1,2 +1,24 @@
|
||||
package com.alterdekim.game.storage;public class StorageService {
|
||||
package com.alterdekim.game.storage;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public interface StorageService {
|
||||
|
||||
void init();
|
||||
|
||||
void store(MultipartFile file);
|
||||
|
||||
Stream<Path> loadAll();
|
||||
|
||||
Path load(String filename);
|
||||
|
||||
Resource loadAsResource(String filename);
|
||||
|
||||
void deleteAll();
|
||||
|
||||
}
|
||||
|
||||
|
506
src/main/resources/static/css/game.css
Normal file
506
src/main/resources/static/css/game.css
Normal file
@ -0,0 +1,506 @@
|
||||
body {
|
||||
font-size: 14px;
|
||||
font-family: 'Open Sans',tahoma,arial,sans-serif;
|
||||
}
|
||||
|
||||
._mbtn {
|
||||
text-align: center;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-bottom: auto;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.dropbox {
|
||||
width: 17%;
|
||||
position: absolute;
|
||||
margin-top: 2%;
|
||||
}
|
||||
|
||||
.dropbox div {
|
||||
background-color: white;
|
||||
color: grey;
|
||||
font-size: 16pt;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.stars {
|
||||
position: fixed;
|
||||
width: 55px;
|
||||
text-align: center;
|
||||
display: inline-flex;
|
||||
margin: auto;
|
||||
color: white;
|
||||
}
|
||||
|
||||
._star {
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
margin-top: -10px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.hstars {
|
||||
margin-left: -40px;
|
||||
}
|
||||
|
||||
._hstar {
|
||||
margin-top: -50px;
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
._vstar {
|
||||
margin-top: -110px;
|
||||
}
|
||||
|
||||
._hhstar {
|
||||
margin-left: 60px;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: #262c2f;
|
||||
overflow: hidden!important;
|
||||
}
|
||||
#chatinput {
|
||||
width: 511px;
|
||||
height: 24px;
|
||||
background-color:rgba(0, 0, 0, 0.1);
|
||||
color: white;
|
||||
border: none;
|
||||
outline:none;
|
||||
font-size:16px;
|
||||
}
|
||||
#log {
|
||||
position: fixed;
|
||||
overflow-x: hidden;
|
||||
overflow: scroll;
|
||||
width: 511px;
|
||||
height: 480px;
|
||||
color: white;
|
||||
}
|
||||
#loading {
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: #262c2f;
|
||||
overflow: hidden!important;
|
||||
z-index: 99;
|
||||
}
|
||||
.table {
|
||||
display: grid;
|
||||
grid-column-gap: 2px;
|
||||
grid-row-gap: 2px;
|
||||
}
|
||||
.corner {
|
||||
background-color: white;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
.fv {
|
||||
background-color: #f5f5f5;
|
||||
width: 100px;
|
||||
height: 55px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.fh {
|
||||
background-color: #f5f5f5;
|
||||
width: 55px;
|
||||
height: 100px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.cost {
|
||||
background-color: white;
|
||||
text-align: center;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* .cost.red {
|
||||
background-color: #CC0000;
|
||||
color:#FFFFFF
|
||||
}
|
||||
|
||||
.cost.green {
|
||||
background-color: #00CC00;
|
||||
color:#FFFFFF
|
||||
} */
|
||||
|
||||
.up {
|
||||
margin-top: 18px;
|
||||
}
|
||||
.iconH {
|
||||
width: 55px;
|
||||
height: 100px;
|
||||
position:relative;
|
||||
}
|
||||
.iconV {
|
||||
width: 100px;
|
||||
height: 55px;
|
||||
position:relative;
|
||||
/* background-image: url("http://localhost/monopoly/fields/land_rover.png"); */
|
||||
}
|
||||
|
||||
.horImg {
|
||||
/* width:55px;
|
||||
height:55px; */
|
||||
width:55px;
|
||||
position:absolute;
|
||||
left:25px;
|
||||
}
|
||||
|
||||
.vertImg {
|
||||
/* width:55px;
|
||||
height:55px; */
|
||||
width:55px;
|
||||
-webkit-transform:rotate(-90deg);
|
||||
-moz-transform: rotate(-90deg);
|
||||
-ms-transform: rotate(-90deg);
|
||||
-o-transform: rotate(-90deg);
|
||||
transform: rotate(-90deg);
|
||||
position:absolute;
|
||||
top:25px;
|
||||
}
|
||||
|
||||
.rcost {
|
||||
float: right;
|
||||
background-color: white;
|
||||
width: 55px;
|
||||
height: 18px;
|
||||
transform: rotate(90deg);
|
||||
margin-top: 18px;
|
||||
margin-left: 82px;
|
||||
text-align: center;
|
||||
color: white;
|
||||
}
|
||||
.lcost {
|
||||
float: left;
|
||||
background-color: white;
|
||||
width: 55px;
|
||||
height: 18px;
|
||||
transform: rotate(90deg);
|
||||
margin-top: 18px;
|
||||
margin-left: -38px;
|
||||
text-align: center;
|
||||
color: white;
|
||||
}
|
||||
.table_center {
|
||||
grid-row-start: 2;
|
||||
grid-column-start: 2;
|
||||
grid-column-end: 11;
|
||||
grid-row-end: 11;
|
||||
}
|
||||
.players {
|
||||
margin-right: 80px;
|
||||
}
|
||||
.player {
|
||||
margin-top: 10px;
|
||||
background-color: #15191a;
|
||||
width: 173px;
|
||||
height: 130px;
|
||||
color: white;
|
||||
text-align: center;
|
||||
font-size: 22px;
|
||||
padding: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.player-turn {
|
||||
width: 193px;
|
||||
height: 150px;
|
||||
}
|
||||
.game {
|
||||
display: inline-flex;
|
||||
}
|
||||
/*
|
||||
chip colors:
|
||||
red - #f2454a
|
||||
blue - #00adef
|
||||
green - #73d249
|
||||
purple - #c874ed
|
||||
orange - #e19d22
|
||||
*/
|
||||
|
||||
.font-red { color: #f2454a; }
|
||||
.font-blue { color: #00adef; }
|
||||
.font-green { color: #73d249; }
|
||||
.font-purple { color: #c874ed; }
|
||||
.font-orange { color: #e19d22; }
|
||||
|
||||
.red { background-color: #f2454a; }
|
||||
.blue { background-color: #00adef; }
|
||||
.green { background-color: #73d249; }
|
||||
.purple { background-color: #c874ed; }
|
||||
.orange { background-color: #e19d22; }
|
||||
|
||||
|
||||
.chips {
|
||||
/* background-color:#ff0000; */
|
||||
width:100%;
|
||||
height:100%;
|
||||
left:173px;
|
||||
top:0px;
|
||||
position:absolute;
|
||||
/* border:1px solid green; */
|
||||
|
||||
|
||||
}
|
||||
|
||||
.chip {
|
||||
box-shadow: 0 0 10px rgba(0,0,0,1);
|
||||
border-radius: 50px;
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
z-index: 98;
|
||||
grid-row-start: 1;
|
||||
grid-row-end: 11;
|
||||
grid-column-start: 1;
|
||||
grid-column-end: 11;
|
||||
margin-top: 18px;
|
||||
}
|
||||
|
||||
.chipMask {
|
||||
border-radius: 50px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
z-index: 99;
|
||||
background-color: black;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.btn_string {
|
||||
display: inline-flex;
|
||||
padding-left:5px;
|
||||
}
|
||||
|
||||
|
||||
#cell_descr {
|
||||
z-index:100;
|
||||
border-radius:10px;
|
||||
width: 202px;
|
||||
background-color:#ffffff;
|
||||
position:absolute;
|
||||
box-shadow: 0px -5px 10px rgba(10,10,10,0.3);
|
||||
background-color:#ffffff;
|
||||
padding-bottom:10px;
|
||||
}
|
||||
|
||||
.cell_descr_top {
|
||||
background-color:#646d79;
|
||||
width:202px;
|
||||
height:57px;
|
||||
border-radius:10px 10px 0px 0px;
|
||||
}
|
||||
|
||||
.rent_table {
|
||||
color:#646d79;
|
||||
font-size:11px;
|
||||
background-color:#ffffff;
|
||||
margin-left:10px;
|
||||
margin-right:10px;
|
||||
}
|
||||
|
||||
.currency {
|
||||
color:#adb3bb;
|
||||
}
|
||||
|
||||
.star {
|
||||
font-size:14px;
|
||||
}
|
||||
|
||||
.star_big {
|
||||
color:#fdbd00;
|
||||
font-size:18px;
|
||||
}
|
||||
|
||||
.cell_descr_title {
|
||||
color:#ffffff;
|
||||
font-size:16px;
|
||||
width:202px;
|
||||
padding-left: 10px;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 4px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.cell_descr_subtitle {
|
||||
color:#e0e0e0;
|
||||
font-size:9px;
|
||||
width:202px;
|
||||
padding-left: 10px;
|
||||
padding-top: 0px;
|
||||
padding-bottom: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
#dialog {
|
||||
position: fixed;
|
||||
width: 491px;
|
||||
/* height: 200px;*/
|
||||
background-color: white;
|
||||
margin-left:10px;
|
||||
margin-right:10px;
|
||||
margin-top:10px;
|
||||
padding-bottom:10px;
|
||||
border-radius:10px;
|
||||
color:#646d79;
|
||||
}
|
||||
|
||||
#dialog_title {
|
||||
font-size: 18px;
|
||||
margin: 10px;
|
||||
}
|
||||
#dialog_desc {
|
||||
font-size: 12px;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
#contract {
|
||||
width: 511px;
|
||||
height: 511px;
|
||||
background-color: white;
|
||||
position: fixed;
|
||||
color: #646d79;
|
||||
font-size: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.btn {
|
||||
border-radius: 5px;
|
||||
font-size: 12px;
|
||||
color: white;
|
||||
width: 220px;
|
||||
text-align: center;
|
||||
padding: 6px 4px;
|
||||
margin: 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn_long {
|
||||
border-radius: 5px;
|
||||
font-size: 12px;
|
||||
color: white;
|
||||
width: 460px;
|
||||
text-align: center;
|
||||
padding: 6px 4px;
|
||||
margin: 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
[contenteditable="true"].single-line {
|
||||
white-space: nowrap;
|
||||
width:100px;
|
||||
overflow: hidden;
|
||||
}
|
||||
[contenteditable="true"].single-line br {
|
||||
display:none;
|
||||
|
||||
}
|
||||
[contenteditable="true"].single-line * {
|
||||
display:inline;
|
||||
white-space:nowrap;
|
||||
}
|
||||
|
||||
.btn.green, .btn_long.green {
|
||||
background-color: #00c29e;
|
||||
|
||||
}
|
||||
|
||||
.btn.grey, .btn_long.grey {
|
||||
background-color: #eceef1;
|
||||
color:#646d79;
|
||||
}
|
||||
|
||||
p.contract-player {
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.contract_money {
|
||||
background: transparent;
|
||||
border: none;
|
||||
outline: none;
|
||||
appearance: textfield;
|
||||
font-size: 20px;
|
||||
color: #646d79;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: white;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin-top: 50%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.diceFace {
|
||||
position:absolute;
|
||||
width:200px;
|
||||
height:200px;
|
||||
text-align:center;
|
||||
line-height:200px;
|
||||
font-size:45px;
|
||||
}
|
||||
|
||||
.dice_view {
|
||||
position: absolute;
|
||||
width:200px;
|
||||
height:200px;
|
||||
perspective:600px;
|
||||
transform: scale(0.5);
|
||||
}
|
||||
|
||||
.dice {
|
||||
width:200px;
|
||||
height:200px;
|
||||
position:relative;
|
||||
transform-style:preserve-3d;
|
||||
transition: transform 1s;
|
||||
}
|
||||
|
||||
.dice_front {
|
||||
background-image: url("monopoly/cube/1.jpg");
|
||||
background-size: 200px;
|
||||
transform:rotateY(0deg) translateZ(100px);
|
||||
}
|
||||
.dice_right {
|
||||
background-image: url("monopoly/cube/2.jpg");
|
||||
background-size: 200px;
|
||||
transform:rotateY(90deg) translateZ(100px);
|
||||
}
|
||||
.dice_back {
|
||||
background-image: url("monopoly/cube/3.jpg");
|
||||
background-size: 200px;
|
||||
transform:rotateY(180deg) translateZ(100px);
|
||||
}
|
||||
.dice_left {
|
||||
background-image: url("monopoly/cube/4.jpg");
|
||||
background-size: 200px;
|
||||
transform:rotateY(-90deg) translateZ(100px);
|
||||
}
|
||||
.dice_top {
|
||||
background-image: url("monopoly/cube/5.jpg");
|
||||
background-size: 200px;
|
||||
transform:rotateX(90deg) translateZ(100px);
|
||||
}
|
||||
.dice_bottom {
|
||||
background-image: url("monopoly/cube/6.jpg");
|
||||
background-size: 200px;
|
||||
transform:rotateX(-90deg) translateZ(100px);
|
||||
}
|
||||
|
||||
.timeout {
|
||||
position: absolute;
|
||||
left: 150px;
|
||||
font-size: 15px;
|
||||
}
|
52
src/main/resources/static/css/style.css
Normal file
52
src/main/resources/static/css/style.css
Normal file
@ -0,0 +1,52 @@
|
||||
.navbar {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
html, body {
|
||||
font-family: 'Montserrat', sans-serif;
|
||||
background-color: #f4f4f5;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
footer {
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
background-color: #e6e9ed;
|
||||
}
|
||||
|
||||
|
||||
.nav-item {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.nav-item:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.btn-outline-primary {
|
||||
border-color: transparent;
|
||||
color: #5b5d67;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: #37bc9d;
|
||||
color: #fff;
|
||||
border-color: #37bc9d;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background-color: rgba(55,188,157,0.85);
|
||||
border-color: rgba(55,188,157,0.85);
|
||||
}
|
||||
|
||||
.btn-outline-primary:hover {
|
||||
background-color: rgba(55,188,157,0.85);
|
||||
border-color: rgba(55,188,157,0.85);
|
||||
}
|
||||
|
||||
.navbar-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 0.2em;
|
||||
}
|
8
src/main/resources/templates/fragments/essentials.html
Normal file
8
src/main/resources/templates/fragments/essentials.html
Normal file
@ -0,0 +1,8 @@
|
||||
<th:block th:fragment="essentials">
|
||||
<script type="module" src="https://unpkg.com/ionicons@7.1.0/dist/ionicons/ionicons.esm.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
|
||||
<script>
|
||||
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
|
||||
const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl))
|
||||
</script>
|
||||
</th:block>
|
24
src/main/resources/templates/fragments/footer.html
Normal file
24
src/main/resources/templates/fragments/footer.html
Normal file
@ -0,0 +1,24 @@
|
||||
<th:block th:fragment="footer">
|
||||
<footer class="bg-light text-center text-lg-start">
|
||||
<div class="container p-4">
|
||||
<div class="row d-flex justify-content-center">
|
||||
<div class="col-lg-4 col-md-12 mb-4 mb-md-0">
|
||||
<h6 class="text-uppercase">Nosedive</h6>
|
||||
<p>
|
||||
Nosedive is a free online game.
|
||||
All brands and trademarks on this page belong to their respective owners and are advertised.
|
||||
</p>
|
||||
</div>
|
||||
<div class="mx-auto col-lg-6 col-md-12 mb-4 mb-md-0">
|
||||
<h6 class="text-uppercase">Social</h6>
|
||||
<p>
|
||||
Follow Monopoly on social networks to stay up to date with game updates.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center p-3">
|
||||
© 2024 Copyright: alterdekim
|
||||
</div>
|
||||
</footer>
|
||||
</th:block>
|
8
src/main/resources/templates/fragments/head.html
Normal file
8
src/main/resources/templates/fragments/head.html
Normal file
@ -0,0 +1,8 @@
|
||||
<th:block th:fragment="head">
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title th:text="${title} ? ${title} : 'Nosedive'"></title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
||||
<link href="https://fonts.googleapis.com/css?family=Montserrat:400,500,800" rel="stylesheet">
|
||||
<link href="/static/css/style.css" rel="stylesheet">
|
||||
</th:block>
|
32
src/main/resources/templates/fragments/navbar.html
Normal file
32
src/main/resources/templates/fragments/navbar.html
Normal file
@ -0,0 +1,32 @@
|
||||
<th:block th:fragment="navbar">
|
||||
<nav class="navbar navbar-expand-lg navbar-light">
|
||||
<div class="container-fluid">
|
||||
<div class="collapse navbar-collapse justify-content-center" id="navbarNav">
|
||||
<a class="navbar-brand" href="/">Nosedive</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<button class="btn btn-primary navbar-btn">
|
||||
<ion-icon name="game-controller-outline"></ion-icon>
|
||||
<span>Games</span>
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<button class="btn btn-outline-primary navbar-btn">
|
||||
<ion-icon name="people-outline"></ion-icon>
|
||||
<span>Friends</span>
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<button class="btn btn-outline-primary navbar-btn">
|
||||
<ion-icon name="briefcase-outline"></ion-icon>
|
||||
<span>Inventory</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</th:block>
|
272
src/main/resources/templates/game.html
Normal file
272
src/main/resources/templates/game.html
Normal file
@ -0,0 +1,272 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html xmlns:th="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta charset="utf-8"></meta>
|
||||
<link rel="stylesheet" href="/static/css/game.css">
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
|
||||
<title>Монополия онлайн</title>
|
||||
<link rel="shortcut icon" type="image/png" href="../static/images/favicon.ico"/>
|
||||
<div id="contract_field" style="display: none">
|
||||
<div style="display: inline-flex;">
|
||||
<div class="contract_image" style="margin-left: 10px"><img src="../static/images/mitsubishi.png" style="width: 55px;"></div>
|
||||
<div class="contract_info" style="margin-left: 10px; display: inline-block; text-align: left">
|
||||
<div class="contract_name">Mitsubishi</div>
|
||||
<div class="contract_cost"style="font-size: 14px">3.000$</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</head>
|
||||
<body>
|
||||
<!--<div id="loading" style="width: 100%; height: 100%;">
|
||||
<div style="text-align: center">
|
||||
<p id="loading_status" style="color: white">Идёт загрузка игры...</p>
|
||||
<div id="returnBtn" onclick="window.location = '/games?gtype=monopoly'" class="btn green" style="margin-top: 5px; display: none; margin-bottom: auto; margin-left: auto; margin-right: auto;">Назад</div>
|
||||
</div>
|
||||
</div>-->
|
||||
<div class="wrapper" style="width: 100%; height: 100%">
|
||||
<div class="game">
|
||||
<div class="players">
|
||||
<th:block th:each="p: ${players}">
|
||||
<div class="player" data-pid="${p.id}" onClick="drop(this)">
|
||||
<p class="timeout"></p>
|
||||
<p class="nickname" th:text="${p.name}"></p>
|
||||
<p class="money" th:text="${p.money}"></p>
|
||||
<div class="dropbox" style="display: none"></div> <!-- margin-top: -35px; -->
|
||||
</div>
|
||||
</th:block>
|
||||
</div>
|
||||
<div class="table">
|
||||
<div class="table_center">
|
||||
|
||||
<!--<div id="log" style="position: fixed; padding: 5px; overflow: scroll;">
|
||||
<div style="display: inline-flex;"><p class="font-red">Name: </p><p> lol</p></div>
|
||||
<div style="display: inline-flex;"><p class="font-red">Name: </p><p> lol</p></div>
|
||||
<div style="display: inline-flex;"><p class="font-red">Name: </p><p> lol</p></div>
|
||||
<div style="display: inline-flex;"><p class="font-red">Name: </p><p> lol</p></div>
|
||||
</div> -->
|
||||
<div id="log">
|
||||
<!-- <div>Игрок <span style="color: red">lol</span> ходит</div> -->
|
||||
</div>
|
||||
<div style="position: absolute; margin-top: 485px" >
|
||||
<input placeholder="Напишите в чат" type="text" id="chatinput"/>
|
||||
</div>
|
||||
|
||||
<div id="dice_cubes" style="margin-top: 100px; display: none;">
|
||||
<div class="dice_view" style="margin-left: 100px;">
|
||||
<div class="dice" id="first_dice">
|
||||
<div class="diceFace dice_front"></div>
|
||||
<div class="diceFace dice_right"></div>
|
||||
<div class="diceFace dice_back"></div>
|
||||
<div class="diceFace dice_left"></div>
|
||||
<div class="diceFace dice_top"></div>
|
||||
<div class="diceFace dice_bottom"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dice_view" style="margin-left: 225px; margin-top: 125px">
|
||||
<div class="dice" id="second_dice">
|
||||
<div class="diceFace dice_front"></div>
|
||||
<div class="diceFace dice_right"></div>
|
||||
<div class="diceFace dice_back"></div>
|
||||
<div class="diceFace dice_left"></div>
|
||||
<div class="diceFace dice_top"></div>
|
||||
<div class="diceFace dice_bottom"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="contract" style="display: none;">
|
||||
<p>Договор</p>
|
||||
<div style="display: inline-flex; width: 511px">
|
||||
<p style="font-size: 16px; text-align: left; margin-left: 10px" class="font-red contract-player">Вы: </p>
|
||||
<p style="font-size: 16px; text-align: left; margin-left: 230px" class="font-green contract-player">User: </p>
|
||||
</div>
|
||||
<div id="my_contract_fields" style="position: absolute; display: grid">
|
||||
<div style="display: inline-flex;">
|
||||
<div style="margin-left: 10px"><img src="../static/images/money.png" style="width: 55px;"></div>
|
||||
<div style="margin-left: 10px; display: inline-block; text-align: left">
|
||||
<input type="number" id="self_money" class="contract_money single-line" value="0" />
|
||||
<div style="font-size: 14px">Наличные</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--<div style="display: inline-flex;">
|
||||
<div style="margin-left: 10"><img src="images/mitsubishi.png" style="width: 55px;"></div>
|
||||
<div style="margin-left: 10; display: inline-block; text-align: left">
|
||||
<div>Mitsubishi</div>
|
||||
<div style="font-size: 14">3.000$</div>
|
||||
</div>
|
||||
</div>-->
|
||||
</div>
|
||||
<div id="contract_fields" style="position: absolute; display: grid; margin-left: 260px">
|
||||
<div style="display: inline-flex;">
|
||||
<div style="margin-left: 10px"><img src="../static/images/money.png" style="width: 55px;"></div>
|
||||
<div style="margin-left: 10px; display: inline-block; text-align: left">
|
||||
<input type="number" id="other_money" class="contract_money single-line" value="0" />
|
||||
<div style="font-size: 14px">Наличные</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr width="1" size="320" style="height: 60%">
|
||||
<div style="display: flex; width: 100%">
|
||||
<div style="width: 33.3%">
|
||||
<div id="allSelfMoney" style="font-size: 16px;">0$</div>
|
||||
</div>
|
||||
<div style="width: 33.3%">
|
||||
<div style="font-size: 16px;">Общая сумма</div>
|
||||
</div>
|
||||
<div style="width: 33.3%">
|
||||
<div id="allOtherMoney" style="font-size: 16px;">0$</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="btn_string">
|
||||
<div id="contractAgree" onClick="contractAgree()" class="btn green" style="width: 100px;">Принять</div><div id="contractDeny" onClick="contractDeny()" class="btn red" style="width: 100px;">Отказать</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: none;" id="dialog" class="dialogturn">
|
||||
<p id="dialog_title">Покупаем?</p>
|
||||
<p id="dialog_desc">Lorem ipsum dolor sit amet...</p></br>
|
||||
<div class="btn_string" id="twobtns">
|
||||
<div id="buybtn" onClick="leftBtnClicked(this)" class="btn green">Купить за 1,000k</div><div id="ignorebtn" onClick="rightBtnClicked()" class="btn grey">На аукцион</div>
|
||||
</div>
|
||||
<div class="btn_string" id="onebtn">
|
||||
<div class="btn_long green" onClick="centerBtnClicked()" id="ogbtn">Купить за 1,000k</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- style="top:270px;left:400px" -->
|
||||
<div style="display: none;" id="cell_descr">
|
||||
<div class="cell_descr_top">
|
||||
<div class="cell_descr_title">Apple</div>
|
||||
<div class="cell_descr_subtitle"><!--Электроника--></div>
|
||||
</div>
|
||||
|
||||
<table class="rent_table">
|
||||
<tr>
|
||||
<div id="buyOrSell" style="display: none;" >
|
||||
<div style="width: 100%; display: inline-flex;">
|
||||
<div onClick="bnf(this)" id="tbbb" class="btn green" style="width: 100%">Купить</div><div onClick="sfof(this)" id="tbsb" style="width: 100%" class="btn red">Продать</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="onlySell" style="display: none;">
|
||||
<div style="width: 100%; display: inline-flex;">
|
||||
<div class="btn_long red" onClick="sfof(this)">Продать</div>
|
||||
</div>
|
||||
</div>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">Стройте филиалы, чтобы увеличить ренту</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Базовая рента</td>
|
||||
<td align="right"><b id="baserent">350</b><span class="currency">k</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="star">★</span></td>
|
||||
<td align="right"><b id="firstrent">1,750</b><span class="currency">k</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="star">★ ★</span></td>
|
||||
<td align="right"><b id="secondrent">5,000</b><span class="currency">k</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="star">★ ★ ★</span></td>
|
||||
<td align="right"><b id="thirdrent">11,050</b><span class="currency">k</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="star">★ ★ ★ ★</span></td>
|
||||
<td align="right"><b id="fourthrent">13,000</b><span class="currency">k</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="star_big">★</span></td>
|
||||
<td align="right"><b id="fithrent">15,000</b><span class="currency">k</span></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="padding-top:10px;">Стоимость поля</td>
|
||||
<td style="padding-top:10px;" align="right"><b id="buyprice">3,500</b><span class="currency">k</span></td>
|
||||
</tr>
|
||||
<tr style="visibility: hidden;">
|
||||
<td>Залог поля</td>
|
||||
<td align="right"><b id="depositprice">1,750</b><span class="currency">k</span></td>
|
||||
</tr>
|
||||
<tr style="visibility: hidden;">
|
||||
<td>Выкуп поля</td>
|
||||
<td align="right"><b id="redeemprice">2,100</b><span class="currency">k</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Покупка филиала</td>
|
||||
<td align="right"><b id="fprice">2,000</b><span class="currency">k</span></td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<!-- <div class="chips"> -->
|
||||
<!-- {262,55} {318,55} {375,55} -->
|
||||
<div class="chip red" data-trow=0 data-tcol=0></div>
|
||||
<div class="chip green" data-trow=0 data-tcol=0></div>
|
||||
<div class="chip blue" data-trow=0 data-tcol=0></div>
|
||||
<div class="chip purple" data-trow=0 data-tcol=0></div>
|
||||
<div class="chip orange" data-trow=0 data-tcol=0></div>
|
||||
<!-- </div> -->
|
||||
<div class="up"><div class="board_field corner"><img src="../static/images/start.png" style="width: 100%; height: 100%;" /></div></div>
|
||||
|
||||
<th:block th:each="f: ${fields_up}">
|
||||
<div th:data-fid="${f.uid}" class="board_field" style="grid-column: @{${f.id}}"> <!-- 2 + -->
|
||||
<div class="cost" th:text="${f.cost}"></div>
|
||||
<div class="fh">
|
||||
<div class="iconH">
|
||||
<img th:src="${f.img}" class="vertImg">
|
||||
</div>
|
||||
<div class="stars">
|
||||
<span class="_star" th:text="${f.stars}"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</th:block>
|
||||
|
||||
<div class="up" style="grid-column: 11"><div class="board_field corner"><img src="../static/images/injail.png" style="width: 100%; height: 100%;" /></div></div>
|
||||
<div class="board_field" style="grid-column: 11"><div class="rcost">0</div><div class="fv"><div class="iconV"><img src="../static/images/lacoste.png" class="horImg"></div><div class="stars hstars"><span class="_star _hstar"></span></div></div></div>
|
||||
<div class="board_field" style="grid-column: 11"><div class="rcost">0</div><div class="fv"><div class="iconV"><img src="../static/images/mitsubishi.png" class="horImg"></div><div class="stars hstars"><span class="_star _hstar"></span></div></div></div>
|
||||
<div class="board_field" style="grid-column: 11"><div class="rcost">0</div><div class="fv"><div class="iconV"><img src="../static/images/vw.png" class="horImg"></div></div><div class="stars hstars"><span class="_star _hstar"></span></div></div>
|
||||
<div class="board_field" style="grid-column: 11"><div class="rcost">0</div><div class="fv"><div class="iconV"><img src="../static/images/mcdonalds.svg" class="horImg"></div><div class="stars hstars"><span class="_star _hstar"></span></div></div></div>
|
||||
<div class="board_field" style="grid-column: 11"><div class="rcost">0</div><div class="fv"><div class="iconV"><img src="../static/images/apple.png" class="horImg"></div><div class="stars hstars"><span class="_star _hstar"></span></div></div></div>
|
||||
<div class="board_field" style="grid-column: 11"><div class="rcost">0</div><div class="fv"><div class="iconV"><img src="../static/images/vw.png" class="horImg"></div><div class="stars hstars"><span class="_star _hstar"></span></div></div></div>
|
||||
<div class="board_field" style="grid-column: 11"><div class="rcost">0</div><div class="fv"><div class="iconV"><img src="../static/images/macdonalds.png" class="horImg"></div><div class="stars hstars"><span class="_star _hstar"></span></div></div></div>
|
||||
<div class="board_field" style="grid-column: 11"><div class="rcost">0</div><div class="fv"><div class="iconV"><img src="../static/images/nike.png" class="horImg"></div><div class="stars hstars"><span class="_star _hstar"></span></div></div></div>
|
||||
<div class="board_field" style="grid-column: 11"><div class="rcost">0</div><div class="fv"><div class="iconV"><img src="../static/images/pepsi.png" class="horImg"></div><div class="stars hstars"><span class="_star _hstar"></span></div></div></div>
|
||||
<div class="board_field corner" style="grid-column: 11"><img src="../static/images/parking.png" style="width:100%; height:100%;" /></div>
|
||||
<div class="board_field" style="grid-column: 2; grid-row: 11;"><div class="fh"><div class="iconH"><img src="../static/images/nike.png" class="vertImg"></div><div class="stars"><span class="_star _vstar"></span></div></div><div class="cost">0</div></div>
|
||||
<div class="board_field" style="grid-column: 3; grid-row: 11;"><div class="fh"><div class="iconH"><img src="../static/images/mitsubishi.png" class="vertImg"></div><div class="stars"><span class="_star _vstar"></span></div></div><div class="cost">0</div></div>
|
||||
<div class="board_field" style="grid-column: 4; grid-row: 11;"><div class="fh"><div class="iconH"><img src="../static/images/peugeot.png" class="vertImg"></div><div class="stars"><span class="_star _vstar"></span></div></div><div class="cost">0</div></div>
|
||||
<div class="board_field" style="grid-column: 5; grid-row: 11;"><div class="fh"><div class="iconH"><img src="../static/images/vw.png" class="vertImg"></div><div class="stars"><span class="_star _vstar"></span></div></div><div class="cost">0</div></div>
|
||||
<div class="board_field" style="grid-column: 6; grid-row: 11;"><div class="fh"><div class="iconH"><img src="../static/images/linux.png" class="vertImg"></div><div class="stars"><span class="_star _vstar"></span></div></div><div class="cost">0</div></div>
|
||||
<div class="board_field" style="grid-column: 7; grid-row: 11;"><div class="fh"><div class="iconH"><img src="../static/images/apple.png" class="vertImg"></div><div class="stars"><span class="_star _vstar"></span></div></div><div class="cost">0</div></div>
|
||||
<div class="board_field" style="grid-column: 8; grid-row: 11;"><div class="fh"><div class="iconH"><img src="../static/images/burger_king.svg" class="vertImg"></div><div class="stars"><span class="_star _vstar"></span></div></div><div class="cost">0</div></div>
|
||||
<div class="board_field" style="grid-column: 9; grid-row: 11;"><div class="fh"><div class="iconH"><img src="../static/images/lacoste.png" class="vertImg"></div><div class="stars"><span class="_star _vstar"></span></div></div><div class="cost">0</div></div>
|
||||
<div class="board_field" style="grid-column: 10; grid-row: 11;"><div class="fh"><div class="iconH"><img src="../static/images/pepsi.png" class="vertImg"></div><div class="stars"><span class="_star _vstar"></span></div></div><div class="cost">0</div></div>
|
||||
<div class="board_field corner" style="grid-column: 1; grid-row: 11"><img src="../static/images/gotojail.png" style="width: 100%; height: 100%;" /></div>
|
||||
<div class="board_field" style="grid-column: 1; grid-row: 10;"><div class="lcost">0</div><div class="fv"><div class="iconV"><img src="../static/images/peugeot.png" class="horImg"></div><div class="stars hstars _hhstar"><span class="_star _hstar"></span></div></div></div>
|
||||
<div class="board_field" style="grid-column: 1; grid-row: 9;"><div class="lcost">0</div><div class="fv"><div class="iconV"><img src="../static/images/apple.png" class="horImg"></div><div class="stars hstars _hhstar"><span class="_star _hstar"></span></div></div></div>
|
||||
<div class="board_field" style="grid-column: 1; grid-row: 8;"><div class="lcost">0</div><div class="fv"><div class="iconV"><img src="../static/images/linux.png" class="horImg"></div><div class="stars hstars _hhstar"><span class="_star _hstar"></span></div></div></div>
|
||||
<div class="board_field" style="grid-column: 1; grid-row: 7;"><div class="lcost">0</div><div class="fv"><div class="iconV"><img src="../static/images/macdonalds.png" class="horImg"></div><div class="stars hstars _hhstar"><span class="_star _hstar"></span></div></div></div>
|
||||
<div class="board_field" style="grid-column: 1; grid-row: 6;"><div class="lcost">0</div><div class="fv"><div class="iconV"><img src="../static/images/pepsi.png" class="horImg"></div><div class="stars hstars _hhstar"><span class="_star _hstar"></span></div></div></div>
|
||||
<div class="board_field" style="grid-column: 1; grid-row: 5;"><div class="lcost">0</div><div class="fv"><div class="iconV"><img src="../static/images/burger_king.svg" class="horImg"></div><div class="stars hstars _hhstar"><span class="_star _hstar"></span></div></div></div>
|
||||
<div class="board_field" style="grid-column: 1; grid-row: 4;"><div class="lcost">0</div><div class="fv"><div class="iconV"><img src="../static/images/mitsubishi.png" class="horImg"></div><div class="stars hstars _hhstar"><span class="_star _hstar"></span></div></div></div>
|
||||
<div class="board_field" style="grid-column: 1; grid-row: 3;"><div class="lcost">0</div><div class="fv"><div class="iconV"><img src="../static/images/adidas.png" class="horImg"></div><div class="stars hstars _hhstar"><span class="_star _hstar"></span></div></div></div>
|
||||
<div class="board_field" style="grid-column: 1; grid-row: 2;"><div class="lcost">0</div><div class="fv"><div class="iconV"><img src="../static/images/lacoste.png" class="horImg"></div><div class="stars hstars _hhstar"><span class="_star _hstar"></span></div></div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<footer>
|
||||
<script type="text/javascript" src="/games/cookie.js"></script>
|
||||
<script type="text/javascript" src="/games/db.js"></script>
|
||||
<script type="text/javascript" src="/games/methods.js"></script>
|
||||
<script type="text/javascript" src="game.js"></script>
|
||||
<script type="text/javascript" src="/static/javascript/scale.js"></script>
|
||||
</footer>
|
||||
</html>
|
36
src/main/resources/templates/login.html
Normal file
36
src/main/resources/templates/login.html
Normal file
@ -0,0 +1,36 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<th:block th:insert="~{fragments/head}"></th:block>
|
||||
</head>
|
||||
<body>
|
||||
<th:block th:insert="~{fragments/navbar}"></th:block>
|
||||
|
||||
<div class="container mt-5 min-vh-100" style="max-width: 330px;">
|
||||
<h4 class="text-center">Login</h4>
|
||||
<th:block th:if="${param.error}">
|
||||
<div class="alert alert-danger" role="alert" th:text="#{login_error}"></div>
|
||||
</th:block>
|
||||
<th:block th:if="${error}">
|
||||
<div class="alert alert-danger" role="alert" th:text="#{login_error}"></div>
|
||||
</th:block>
|
||||
<form method="post" th:action="@{/login}">
|
||||
<div class="mb-3">
|
||||
<label for="username" class="form-label"><ion-icon name="at-outline"></ion-icon> Nickname</label>
|
||||
<input type="text" name="username" class="form-control" id="username">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label"><ion-icon name="key-outline"></ion-icon> Password</label>
|
||||
<input type="password" name="password" class="form-control" id="password">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<p>Don't have an account? <a href="/signup">Sign up</a></p>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Login</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<th:block th:insert="~{fragments/footer}"></th:block>
|
||||
<th:block th:insert="~{fragments/essentials}"></th:block>
|
||||
</body>
|
||||
</html>
|
48
src/main/resources/templates/signup.html
Normal file
48
src/main/resources/templates/signup.html
Normal file
@ -0,0 +1,48 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<th:block th:insert="~{fragments/head}"></th:block>
|
||||
</head>
|
||||
<body>
|
||||
<th:block th:insert="~{fragments/navbar}"></th:block>
|
||||
|
||||
<div class="container mt-5 min-vh-100" style="max-width: 330px;">
|
||||
<h4 class="text-center">Sign up</h4>
|
||||
<th:block th:if="${param.error}">
|
||||
<div class="alert alert-danger" role="alert" th:text="#{login_error}"></div>
|
||||
</th:block>
|
||||
<th:block th:if="${error}">
|
||||
<div class="alert alert-danger" role="alert" th:text="#{login_error}"></div>
|
||||
</th:block>
|
||||
<form method="post" th:action="@{/signup}" th:object="${user}">
|
||||
<div class="mb-3">
|
||||
<label for="username" class="form-label" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-title="This nickname will see other users and they will be able tag you like: @username"><ion-icon name="at-outline"></ion-icon> Nickname</label>
|
||||
<input type="text" name="username" class="form-control" id="username">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-title="Password must have at least 7 symbols, include letters, digits, any of special symbols (#$%:;~|&^!_\/-+*)"><ion-icon name="key-outline"></ion-icon> Password</label>
|
||||
<input type="password" name="password" class="form-control" id="password">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="invite_input">Invite code</label>
|
||||
<input type="text" name="invite_code" class="form-control" id="invite_input">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" value="" id="flexCheckDefault">
|
||||
<label class="form-check-label" for="flexCheckDefault">
|
||||
I have read the terms and conditions
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Sign up</button>
|
||||
<div class="mb-3">
|
||||
<p>Already have an account? <a href="/login">Login</a></p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<th:block th:insert="~{fragments/footer}"></th:block>
|
||||
<th:block th:insert="~{fragments/essentials}"></th:block>
|
||||
</body>
|
||||
</html>
|
Loading…
x
Reference in New Issue
Block a user