basic js: store/context, domain models, utils, router
This commit is contained in:
parent
a05e250888
commit
9191e6bf16
@ -1,5 +1,6 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="Build & Run" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot">
|
<configuration default="false" name="Build & Run" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot">
|
||||||
|
<option name="ACTIVE_PROFILES" value="dev" />
|
||||||
<module name="server" />
|
<module name="server" />
|
||||||
<option name="SPRING_BOOT_MAIN_CLASS" value="ru.tubryansk.tdms.TdmsApplication" />
|
<option name="SPRING_BOOT_MAIN_CLASS" value="ru.tubryansk.tdms.TdmsApplication" />
|
||||||
<method v="2">
|
<method v="2">
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="Clean, Build & Run" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot">
|
<configuration default="false" name="Clean, Build & Run" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot">
|
||||||
|
<option name="ACTIVE_PROFILES" value="dev" />
|
||||||
<module name="server" />
|
<module name="server" />
|
||||||
<option name="SPRING_BOOT_MAIN_CLASS" value="ru.tubryansk.tdms.TdmsApplication" />
|
<option name="SPRING_BOOT_MAIN_CLASS" value="ru.tubryansk.tdms.TdmsApplication" />
|
||||||
<method v="2">
|
<method v="2">
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="Run" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot">
|
<configuration default="false" name="Run" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot">
|
||||||
|
<option name="ACTIVE_PROFILES" value="dev" />
|
||||||
<module name="server" />
|
<module name="server" />
|
||||||
<option name="SPRING_BOOT_MAIN_CLASS" value="ru.tubryansk.tdms.TdmsApplication" />
|
<option name="SPRING_BOOT_MAIN_CLASS" value="ru.tubryansk.tdms.TdmsApplication" />
|
||||||
<method v="2" />
|
<method v="2" />
|
||||||
|
|||||||
6
mvnw.cmd
vendored
6
mvnw.cmd
vendored
@ -32,7 +32,7 @@
|
|||||||
@SET __MVNW_ERROR__=
|
@SET __MVNW_ERROR__=
|
||||||
@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
|
@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
|
||||||
@SET PSModulePath=
|
@SET PSModulePath=
|
||||||
@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
|
@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-PageContent -Raw '%~f0'))) -NoNewScope}"`) DO @(
|
||||||
IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
|
IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
|
||||||
)
|
)
|
||||||
@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
|
@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
|
||||||
@ -51,7 +51,7 @@ if ($env:MVNW_VERBOSE -eq "true") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
|
# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
|
||||||
$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
|
$distributionUrl = (Get-PageContent -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
|
||||||
if (!$distributionUrl) {
|
if (!$distributionUrl) {
|
||||||
Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
|
Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
|
||||||
}
|
}
|
||||||
@ -121,7 +121,7 @@ if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
|
|||||||
$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
|
$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
|
||||||
|
|
||||||
# If specified, validate the SHA-256 sum of the Maven distribution zip file
|
# If specified, validate the SHA-256 sum of the Maven distribution zip file
|
||||||
$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
|
$distributionSha256Sum = (Get-PageContent -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
|
||||||
if ($distributionSha256Sum) {
|
if ($distributionSha256Sum) {
|
||||||
if ($USE_MVND) {
|
if ($USE_MVND) {
|
||||||
Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
|
Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
|
||||||
|
|||||||
12
pom.xml
12
pom.xml
@ -3,6 +3,12 @@
|
|||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<name>TDMS</name>
|
||||||
|
<description>Thesis-Defense-Management-System</description>
|
||||||
|
<version>0.0.1</version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-parent</artifactId>
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
@ -12,11 +18,11 @@
|
|||||||
|
|
||||||
<groupId>ru.tubryansk</groupId>
|
<groupId>ru.tubryansk</groupId>
|
||||||
<artifactId>tdms</artifactId>
|
<artifactId>tdms</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1</version>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
<name>TDMS</name>
|
<name>${name}</name>
|
||||||
<description>Thesis-Defense-Management-System</description>
|
<description>${description}</description>
|
||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
<module>web</module>
|
<module>web</module>
|
||||||
|
|||||||
@ -7,12 +7,12 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>ru.tubryansk</groupId>
|
<groupId>ru.tubryansk</groupId>
|
||||||
<artifactId>tdms</artifactId>
|
<artifactId>tdms</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<groupId>ru.tubryansk.tdms</groupId>
|
<groupId>ru.tubryansk.tdms</groupId>
|
||||||
<artifactId>server</artifactId>
|
<artifactId>server</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1</version>
|
||||||
|
|
||||||
<name>TDMS :: SERVER</name>
|
<name>TDMS :: SERVER</name>
|
||||||
|
|
||||||
|
|||||||
@ -1,15 +1,18 @@
|
|||||||
package ru.tubryansk.tdms;
|
package ru.tubryansk.tdms;
|
||||||
|
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
|
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
|
@Slf4j
|
||||||
public class TdmsApplication {
|
public class TdmsApplication {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(TdmsApplication.class, args);
|
ConfigurableApplicationContext context = SpringApplication.run(TdmsApplication.class, args);
|
||||||
|
String staticLocation = context.getEnvironment().getProperty("spring.web.resources.static-locations");
|
||||||
|
log.info("Static location: {}", staticLocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package ru.tubryansk.tdms.config;
|
|||||||
|
|
||||||
import jakarta.servlet.http.HttpSessionEvent;
|
import jakarta.servlet.http.HttpSessionEvent;
|
||||||
import jakarta.servlet.http.HttpSessionListener;
|
import jakarta.servlet.http.HttpSessionListener;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
@ -18,12 +19,12 @@ import org.springframework.security.config.http.SessionCreationPolicy;
|
|||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.context.SecurityContext;
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.security.core.userdetails.User;
|
|
||||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
|
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
|
import org.springframework.web.cors.CorsConfiguration;
|
||||||
|
import org.springframework.web.cors.CorsConfigurationSource;
|
||||||
|
|
||||||
import static org.springframework.security.web.context.HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY;
|
import static org.springframework.security.web.context.HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY;
|
||||||
|
|
||||||
@ -31,19 +32,32 @@ import static org.springframework.security.web.context.HttpSessionSecurityContex
|
|||||||
@Configuration
|
@Configuration
|
||||||
public class SecurityConfiguration {
|
public class SecurityConfiguration {
|
||||||
@Bean
|
@Bean
|
||||||
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
|
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity, AuthenticationManager authenticationManager) throws Exception {
|
||||||
return httpSecurity
|
return httpSecurity
|
||||||
.authorizeHttpRequests(this::configureHttpAuthorization)
|
.authorizeHttpRequests(this::configureHttpAuthorization)
|
||||||
.csrf(AbstractHttpConfigurer::disable)
|
.csrf(AbstractHttpConfigurer::disable)
|
||||||
.cors(AbstractHttpConfigurer::disable)
|
.cors(a -> a.configurationSource(corsConfiguration()))
|
||||||
.authenticationManager(authenticationManager())
|
.authenticationManager(authenticationManager)
|
||||||
.sessionManagement(this::configureSessionManagement)
|
.sessionManagement(this::configureSessionManagement)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public CorsConfigurationSource corsConfiguration() {
|
||||||
|
return request -> {
|
||||||
|
CorsConfiguration corsConfiguration = new CorsConfiguration();
|
||||||
|
corsConfiguration.applyPermitDefaultValues();
|
||||||
|
corsConfiguration.addAllowedMethod("DELETE");
|
||||||
|
corsConfiguration.addAllowedMethod("PUT");
|
||||||
|
corsConfiguration.addAllowedMethod("PATCH");
|
||||||
|
return corsConfiguration;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private void configureHttpAuthorization(AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry httpAuthorization) {
|
private void configureHttpAuthorization(AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry httpAuthorization) {
|
||||||
/* API ROUTES */
|
/* API ROUTES */
|
||||||
httpAuthorization.requestMatchers("/api/diploma-topic/**").permitAll();
|
httpAuthorization.requestMatchers("/api/v1/diploma-topic/**").permitAll();
|
||||||
|
httpAuthorization.requestMatchers("/api/v1/user/**").permitAll();
|
||||||
httpAuthorization.requestMatchers("/api/**").denyAll();
|
httpAuthorization.requestMatchers("/api/**").denyAll();
|
||||||
/* STATIC ROUTES */
|
/* STATIC ROUTES */
|
||||||
httpAuthorization.requestMatchers("/**").permitAll();
|
httpAuthorization.requestMatchers("/**").permitAll();
|
||||||
@ -52,38 +66,29 @@ public class SecurityConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public AuthenticationManager authenticationManager() {
|
public AuthenticationManager authenticationManager(UserDetailsService userDetailsService) {
|
||||||
|
return new ProviderManager(authenticationProvider(userDetailsService));
|
||||||
return new ProviderManager(authenticationProvider());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private AuthenticationProvider authenticationProvider() {
|
private AuthenticationProvider authenticationProvider(UserDetailsService userDetailsService) {
|
||||||
DaoAuthenticationProvider provider = new DaoAuthenticationProvider(passwordEncoder());
|
DaoAuthenticationProvider provider = new DaoAuthenticationProvider(passwordEncoder());
|
||||||
provider.setUserDetailsService(userDetailsService());
|
provider.setUserDetailsService(userDetailsService);
|
||||||
return provider;
|
return provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
private UserDetailsService userDetailsService() {
|
|
||||||
return new InMemoryUserDetailsManager(User.builder()
|
|
||||||
.username("admin")
|
|
||||||
.password("{noop}admin")
|
|
||||||
.authorities("ROLE_STUDENT", "ROLE_TEACHER", "ROLE_CURATOR")
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
private PasswordEncoder passwordEncoder() {
|
private PasswordEncoder passwordEncoder() {
|
||||||
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
|
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
// todo: remove when login/logout is implemented
|
public HttpSessionListener autoAuthenticateUnderAdmin(AuthenticationManager authenticationManager) {
|
||||||
public HttpSessionListener autoAuthenticateUnderAdmin() {
|
|
||||||
return new HttpSessionListener() {
|
return new HttpSessionListener() {
|
||||||
@Override
|
@Override
|
||||||
public void sessionCreated(HttpSessionEvent se) {
|
public void sessionCreated(HttpSessionEvent se) {
|
||||||
|
LoggerFactory.getLogger(this.getClass()).info("Session created {}. Authenticated, as izrailev_v_ya_1", se.getSession().getId());
|
||||||
SecurityContext context = SecurityContextHolder.createEmptyContext();
|
SecurityContext context = SecurityContextHolder.createEmptyContext();
|
||||||
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken("admin", "admin");
|
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken("izrailev_v_ya_1", "1");
|
||||||
Authentication authenticated = authenticationManager().authenticate(authentication);
|
Authentication authenticated = authenticationManager.authenticate(authentication);
|
||||||
context.setAuthentication(authenticated);
|
context.setAuthentication(authenticated);
|
||||||
SecurityContextHolder.setContext(context);
|
SecurityContextHolder.setContext(context);
|
||||||
se.getSession().setAttribute(SPRING_SECURITY_CONTEXT_KEY, context);
|
se.getSession().setAttribute(SPRING_SECURITY_CONTEXT_KEY, context);
|
||||||
|
|||||||
@ -15,9 +15,9 @@ import java.util.List;
|
|||||||
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/diploma-topic/")
|
@RequestMapping("/api/v1/diploma-topic/")
|
||||||
@Validated
|
@Validated
|
||||||
public class DiplomaTopicRestController {
|
public class DiplomaTopicController {
|
||||||
@Autowired
|
@Autowired
|
||||||
private DiplomaTopicService diplomaTopicService;
|
private DiplomaTopicService diplomaTopicService;
|
||||||
|
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
package ru.tubryansk.tdms.controller;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
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.RestController;
|
||||||
|
import ru.tubryansk.tdms.dto.UserDTO;
|
||||||
|
import ru.tubryansk.tdms.entity.User;
|
||||||
|
import ru.tubryansk.tdms.service.UserService;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@Validated
|
||||||
|
@RequestMapping("/api/v1/user")
|
||||||
|
@Slf4j
|
||||||
|
public class UserController {
|
||||||
|
@Autowired
|
||||||
|
private UserService userService;
|
||||||
|
|
||||||
|
@GetMapping("/current")
|
||||||
|
public UserDTO getCurrentUser() {
|
||||||
|
User principal = userService.getCallerPrincipal();
|
||||||
|
return principal != null ? UserDTO.from(principal, true) : UserDTO.fromUnauthenticated();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/logout")
|
||||||
|
public void logout() {
|
||||||
|
userService.logout();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,26 +1,43 @@
|
|||||||
package ru.tubryansk.tdms.dto;
|
package ru.tubryansk.tdms.dto;
|
||||||
|
|
||||||
|
|
||||||
import jakarta.persistence.Column;
|
import lombok.Builder;
|
||||||
import lombok.AllArgsConstructor;
|
import ru.tubryansk.tdms.entity.Role;
|
||||||
import lombok.Data;
|
import ru.tubryansk.tdms.entity.User;
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
|
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
@Data
|
@Builder
|
||||||
@AllArgsConstructor
|
public record UserDTO(
|
||||||
@NoArgsConstructor
|
boolean authenticated,
|
||||||
public class UserDTO {
|
String login,
|
||||||
private Integer id;
|
String password,
|
||||||
private String login;
|
String fullName,
|
||||||
private String password;
|
String email,
|
||||||
private String fullName;
|
String phoneNumber,
|
||||||
private String mail;
|
ZonedDateTime createdAt,
|
||||||
private String numberPhone;
|
ZonedDateTime updatedAt,
|
||||||
private ZonedDateTime createAt;
|
List<String> authorities) {
|
||||||
private ZonedDateTime updateAt;
|
|
||||||
List<Integer> roleId;
|
public static UserDTO fromUnauthenticated() {
|
||||||
|
return UserDTO.builder()
|
||||||
|
.authenticated(false)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UserDTO from(User user, boolean anonymize) {
|
||||||
|
return UserDTO.builder()
|
||||||
|
.authenticated(true)
|
||||||
|
.login(user.getLogin())
|
||||||
|
.password(anonymize ? "" : user.getPassword())
|
||||||
|
.fullName(user.getFullName())
|
||||||
|
.email(user.getMail())
|
||||||
|
.phoneNumber(user.getNumberPhone())
|
||||||
|
.createdAt(user.getCreateAt())
|
||||||
|
.updatedAt(user.getUpdateAt())
|
||||||
|
.authorities(user.getRoles().stream().map(Role::getAuthority).toList())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import lombok.AllArgsConstructor;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@ -14,11 +15,16 @@ import lombok.Setter;
|
|||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@Table(name = "role", schema = "vkr")
|
@Table(name = "role", schema = "vkr")
|
||||||
public class Role {
|
public class Role implements GrantedAuthority {
|
||||||
@Id
|
@Id
|
||||||
@Column(name = "id")
|
@Column(name = "id")
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
private Integer id;
|
private Integer id;
|
||||||
@Column(name = "name", nullable = false)
|
@Column(name = "name", nullable = false)
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAuthority() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,8 +6,12 @@ import lombok.AllArgsConstructor;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
@ -17,7 +21,7 @@ import java.util.List;
|
|||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@Table(name = "user", schema = "vkr")
|
@Table(name = "user", schema = "vkr")
|
||||||
public class User {
|
public class User implements UserDetails {
|
||||||
@Id
|
@Id
|
||||||
@Column(name = "id")
|
@Column(name = "id")
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
@ -36,10 +40,19 @@ public class User {
|
|||||||
private ZonedDateTime createAt;
|
private ZonedDateTime createAt;
|
||||||
@Column(name = "update_at")
|
@Column(name = "update_at")
|
||||||
private ZonedDateTime updateAt;
|
private ZonedDateTime updateAt;
|
||||||
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
||||||
@JoinTable(name = "user_role",schema = "vkr",
|
@JoinTable(name = "user_role",schema = "vkr",
|
||||||
joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
|
joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
|
||||||
inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"))
|
inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"))
|
||||||
private List<Role> userLoyaltyLevels;
|
private List<Role> roles;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||||
|
return roles.stream().map(Role::getAuthority).map(SimpleGrantedAuthority::new).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUsername() {
|
||||||
|
return login;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,11 @@
|
|||||||
|
package ru.tubryansk.tdms.repository;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import ru.tubryansk.tdms.entity.User;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public interface UserRepository extends JpaRepository<User, Integer> {
|
||||||
|
Optional<User> findUserByLogin(String login);
|
||||||
|
}
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
package ru.tubryansk.tdms.service;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpSession;
|
||||||
|
import jakarta.transaction.Transactional;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.authentication.AnonymousAuthenticationToken;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import ru.tubryansk.tdms.entity.User;
|
||||||
|
import ru.tubryansk.tdms.repository.UserRepository;
|
||||||
|
|
||||||
|
import static org.springframework.security.web.context.HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Transactional
|
||||||
|
@Slf4j
|
||||||
|
public class UserService implements UserDetailsService {
|
||||||
|
@Autowired
|
||||||
|
private UserRepository userRepository;
|
||||||
|
@Autowired
|
||||||
|
private HttpServletRequest httpServletRequest;
|
||||||
|
|
||||||
|
public User getCallerPrincipal() {
|
||||||
|
if(!authenticated()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean authenticated() {
|
||||||
|
var authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
return authentication.isAuthenticated() && !(authentication instanceof AnonymousAuthenticationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public User loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||||
|
return userRepository.findUserByLogin(username)
|
||||||
|
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void logout() {
|
||||||
|
HttpSession session = httpServletRequest.getSession(true);
|
||||||
|
// if(session != null) {
|
||||||
|
// session.invalidate();
|
||||||
|
// }
|
||||||
|
session.setAttribute(SPRING_SECURITY_CONTEXT_KEY, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
{
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "db.url",
|
||||||
|
"type": "java.lang.String",
|
||||||
|
"description": "Database url."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "db.user",
|
||||||
|
"type": "java.lang.String",
|
||||||
|
"description": "Database user."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "db.password",
|
||||||
|
"type": "java.lang.String",
|
||||||
|
"description": "Database password."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "db.schema",
|
||||||
|
"type": "java.lang.String",
|
||||||
|
"description": "Database schema."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "application.name",
|
||||||
|
"type": "java.lang.String",
|
||||||
|
"description": "Service name."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "application.version",
|
||||||
|
"type": "java.lang.String",
|
||||||
|
"description": "Service version."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "application.type",
|
||||||
|
"type": "java.lang.String",
|
||||||
|
"description": "Service build type."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "application.domain",
|
||||||
|
"type": "java.lang.String",
|
||||||
|
"description": "Service domain."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "application.port",
|
||||||
|
"type": "java.lang.Integer",
|
||||||
|
"description": "Service port."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "application.protocol",
|
||||||
|
"type": "java.lang.String",
|
||||||
|
"description": "Service protocol."
|
||||||
|
}
|
||||||
|
] }
|
||||||
17
server/src/main/resources/application-dev.yml
Normal file
17
server/src/main/resources/application-dev.yml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
application:
|
||||||
|
type: development
|
||||||
|
port: 8080
|
||||||
|
domain: localhost
|
||||||
|
protocol: http
|
||||||
|
spring:
|
||||||
|
flyway:
|
||||||
|
out-of-order: true
|
||||||
|
web:
|
||||||
|
resources:
|
||||||
|
static-locations: file://${user.dir}/web/dist/
|
||||||
|
chain:
|
||||||
|
cache: false
|
||||||
|
compressed: false
|
||||||
|
server:
|
||||||
|
compression:
|
||||||
|
enabled: false
|
||||||
@ -1,23 +1,36 @@
|
|||||||
|
db:
|
||||||
|
url: jdbc:postgresql://localhost:5432/db
|
||||||
|
user: root
|
||||||
|
password: root
|
||||||
|
schema: vkr
|
||||||
|
application:
|
||||||
|
name: @name@
|
||||||
|
version: @version@
|
||||||
|
type: production
|
||||||
|
port: 80
|
||||||
|
domain: tdms.tu-bryansk.ru
|
||||||
|
protocol: https
|
||||||
|
|
||||||
spring:
|
spring:
|
||||||
application:
|
application:
|
||||||
name: tdms
|
name: tdms
|
||||||
datasource:
|
datasource:
|
||||||
url: jdbc:postgresql://localhost:5432/db
|
url: ${db.url}
|
||||||
username: root
|
username: ${db.user}
|
||||||
password: root
|
password: ${db.password}
|
||||||
driver-class-name: org.postgresql.Driver
|
hikari:
|
||||||
|
schema: ${db.schema}
|
||||||
jpa:
|
jpa:
|
||||||
open-in-view: false
|
open-in-view: false
|
||||||
show-sql: true
|
|
||||||
hibernate.ddl-auto: validate
|
hibernate.ddl-auto: validate
|
||||||
database: postgresql
|
|
||||||
properties.hibernate:
|
|
||||||
pretty_print: true
|
|
||||||
format_sql: true
|
|
||||||
dialect: org.hibernate.dialect.PostgreSQLDialect
|
|
||||||
flyway:
|
flyway:
|
||||||
user: root
|
url: ${db.url}
|
||||||
password: root
|
user: ${db.user}
|
||||||
schemas: vkr
|
password: ${db.password}
|
||||||
|
schemas: ${db.schema}
|
||||||
locations: db.migration
|
locations: db.migration
|
||||||
url: jdbc:postgresql://localhost:5432/db
|
server:
|
||||||
|
port: ${application.port}
|
||||||
|
address: ${application.domain}
|
||||||
|
compression:
|
||||||
|
enabled: true
|
||||||
|
|||||||
@ -0,0 +1 @@
|
|||||||
|
UPDATE vkr.user SET password = '{noop}1';
|
||||||
@ -7,6 +7,6 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<script type="module" src="/src/main.tsx"></script>
|
<script type="module" src="/src/Application.tsx"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
245
web/package-lock.json
generated
245
web/package-lock.json
generated
@ -9,9 +9,11 @@
|
|||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@react-buddy/ide-toolbox": "^2.4.0",
|
"@react-buddy/ide-toolbox": "^2.4.0",
|
||||||
|
"axios": "^1.7.7",
|
||||||
"bootstrap": "^5.3.3",
|
"bootstrap": "^5.3.3",
|
||||||
"mobx": "^6.13.1",
|
"mobx": "^6.13.1",
|
||||||
"mobx-react": "^9.1.1",
|
"mobx-react": "^9.1.1",
|
||||||
|
"mobx-state-router": "^6.0.1",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-bootstrap": "^2.10.4",
|
"react-bootstrap": "^2.10.4",
|
||||||
"react-dom": "^18.3.1"
|
"react-dom": "^18.3.1"
|
||||||
@ -104,12 +106,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/generator": {
|
"node_modules/@babel/generator": {
|
||||||
"version": "7.25.0",
|
"version": "7.25.6",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.0.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.6.tgz",
|
||||||
"integrity": "sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==",
|
"integrity": "sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/types": "^7.25.0",
|
"@babel/types": "^7.25.6",
|
||||||
"@jridgewell/gen-mapping": "^0.3.5",
|
"@jridgewell/gen-mapping": "^0.3.5",
|
||||||
"@jridgewell/trace-mapping": "^0.3.25",
|
"@jridgewell/trace-mapping": "^0.3.25",
|
||||||
"jsesc": "^2.5.1"
|
"jsesc": "^2.5.1"
|
||||||
@ -252,12 +254,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/parser": {
|
"node_modules/@babel/parser": {
|
||||||
"version": "7.25.3",
|
"version": "7.25.6",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.3.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.6.tgz",
|
||||||
"integrity": "sha512-iLTJKDbJ4hMvFPgQwwsVoxtHyWpKKPBrxkANrSYewDPaPpT5py5yeVkgPIJ7XYXhndxJpaA3PyALSXQ7u8e/Dw==",
|
"integrity": "sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/types": "^7.25.2"
|
"@babel/types": "^7.25.6"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"parser": "bin/babel-parser.js"
|
"parser": "bin/babel-parser.js"
|
||||||
@ -322,16 +324,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/traverse": {
|
"node_modules/@babel/traverse": {
|
||||||
"version": "7.25.3",
|
"version": "7.25.6",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.3.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.6.tgz",
|
||||||
"integrity": "sha512-HefgyP1x754oGCsKmV5reSmtV7IXj/kpaE1XYY+D9G5PvKKoFfSbiS4M77MdjuwlZKDIKFCffq9rPU+H/s3ZdQ==",
|
"integrity": "sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.24.7",
|
"@babel/code-frame": "^7.24.7",
|
||||||
"@babel/generator": "^7.25.0",
|
"@babel/generator": "^7.25.6",
|
||||||
"@babel/parser": "^7.25.3",
|
"@babel/parser": "^7.25.6",
|
||||||
"@babel/template": "^7.25.0",
|
"@babel/template": "^7.25.0",
|
||||||
"@babel/types": "^7.25.2",
|
"@babel/types": "^7.25.6",
|
||||||
"debug": "^4.3.1",
|
"debug": "^4.3.1",
|
||||||
"globals": "^11.1.0"
|
"globals": "^11.1.0"
|
||||||
},
|
},
|
||||||
@ -340,9 +342,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/types": {
|
"node_modules/@babel/types": {
|
||||||
"version": "7.25.2",
|
"version": "7.25.6",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.2.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz",
|
||||||
"integrity": "sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==",
|
"integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-string-parser": "^7.24.8",
|
"@babel/helper-string-parser": "^7.24.8",
|
||||||
@ -985,6 +987,14 @@
|
|||||||
"react": "^17.0.0 || ^18.0.0"
|
"react": "^17.0.0 || ^18.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@react-force/utils": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-force/utils/-/utils-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-HdGqlVbNifHN0DL0kybtfuz3oXKk5B8nTkiZwMtMZGrYPOjw0WmHSLT0ptlyXkPUzIrJoRnazL//L3jHZghaCQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@restart/hooks": {
|
"node_modules/@restart/hooks": {
|
||||||
"version": "0.4.16",
|
"version": "0.4.16",
|
||||||
"resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.16.tgz",
|
"resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.16.tgz",
|
||||||
@ -1606,6 +1616,21 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/asynckit": {
|
||||||
|
"version": "0.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||||
|
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
||||||
|
},
|
||||||
|
"node_modules/axios": {
|
||||||
|
"version": "1.7.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz",
|
||||||
|
"integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"follow-redirects": "^1.15.6",
|
||||||
|
"form-data": "^4.0.0",
|
||||||
|
"proxy-from-env": "^1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/balanced-match": {
|
"node_modules/balanced-match": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
@ -1746,6 +1771,17 @@
|
|||||||
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
|
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/combined-stream": {
|
||||||
|
"version": "1.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||||
|
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||||
|
"dependencies": {
|
||||||
|
"delayed-stream": "~1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/concat-map": {
|
"node_modules/concat-map": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||||
@ -1781,7 +1817,6 @@
|
|||||||
"version": "4.3.6",
|
"version": "4.3.6",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz",
|
||||||
"integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==",
|
"integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ms": "2.1.2"
|
"ms": "2.1.2"
|
||||||
},
|
},
|
||||||
@ -1794,12 +1829,28 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/decode-uri-component": {
|
||||||
|
"version": "0.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
|
||||||
|
"integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/deep-is": {
|
"node_modules/deep-is": {
|
||||||
"version": "0.1.4",
|
"version": "0.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
|
||||||
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
|
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/delayed-stream": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/dequal": {
|
"node_modules/dequal": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
|
||||||
@ -2264,6 +2315,14 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/filter-obj": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/find-up": {
|
"node_modules/find-up": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
|
||||||
@ -2300,6 +2359,38 @@
|
|||||||
"integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
|
"integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/follow-redirects": {
|
||||||
|
"version": "1.15.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
|
||||||
|
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"debug": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/form-data": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
||||||
|
"dependencies": {
|
||||||
|
"asynckit": "^0.4.0",
|
||||||
|
"combined-stream": "^1.0.8",
|
||||||
|
"mime-types": "^2.1.12"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/fs.realpath": {
|
"node_modules/fs.realpath": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||||
@ -2428,6 +2519,19 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/history": {
|
||||||
|
"version": "4.10.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz",
|
||||||
|
"integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.1.2",
|
||||||
|
"loose-envify": "^1.2.0",
|
||||||
|
"resolve-pathname": "^3.0.0",
|
||||||
|
"tiny-invariant": "^1.0.2",
|
||||||
|
"tiny-warning": "^1.0.0",
|
||||||
|
"value-equal": "^1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ignore": {
|
"node_modules/ignore": {
|
||||||
"version": "5.3.1",
|
"version": "5.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
|
||||||
@ -2676,6 +2780,25 @@
|
|||||||
"node": ">=8.6"
|
"node": ">=8.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/mime-db": {
|
||||||
|
"version": "1.52.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||||
|
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mime-types": {
|
||||||
|
"version": "2.1.35",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||||
|
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||||
|
"dependencies": {
|
||||||
|
"mime-db": "1.52.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/minimatch": {
|
"node_modules/minimatch": {
|
||||||
"version": "9.0.5",
|
"version": "9.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
|
||||||
@ -2748,11 +2871,30 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/mobx-state-router": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/mobx-state-router/-/mobx-state-router-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-tb2FCzb4Owxh6MQ+FkFC6ntJfM0TcOy2UG/3YBcRm/6hLhr42XG+ijOoQydf9C66DY7ihYVzf8LhSHvr8ie4fA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@react-force/utils": "^2.3.0",
|
||||||
|
"debug": "^4.3.3",
|
||||||
|
"history": "^4.10.1",
|
||||||
|
"path-to-regexp": "^6.2.0",
|
||||||
|
"query-string": "^7.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"mobx": ">=6",
|
||||||
|
"mobx-react-lite": ">=3",
|
||||||
|
"react": ">=16"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/nanoid": {
|
"node_modules/nanoid": {
|
||||||
"version": "3.3.7",
|
"version": "3.3.7",
|
||||||
@ -2887,6 +3029,11 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/path-to-regexp": {
|
||||||
|
"version": "6.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz",
|
||||||
|
"integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ=="
|
||||||
|
},
|
||||||
"node_modules/path-type": {
|
"node_modules/path-type": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
|
||||||
@ -2973,6 +3120,11 @@
|
|||||||
"react": ">=0.14.0"
|
"react": ">=0.14.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/proxy-from-env": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
|
||||||
|
},
|
||||||
"node_modules/punycode": {
|
"node_modules/punycode": {
|
||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||||
@ -2982,6 +3134,23 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/query-string": {
|
||||||
|
"version": "7.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz",
|
||||||
|
"integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==",
|
||||||
|
"dependencies": {
|
||||||
|
"decode-uri-component": "^0.2.2",
|
||||||
|
"filter-obj": "^1.1.0",
|
||||||
|
"split-on-first": "^1.0.0",
|
||||||
|
"strict-uri-encode": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/queue-microtask": {
|
"node_modules/queue-microtask": {
|
||||||
"version": "1.2.3",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||||
@ -3102,6 +3271,11 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/resolve-pathname": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng=="
|
||||||
|
},
|
||||||
"node_modules/reusify": {
|
"node_modules/reusify": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
|
||||||
@ -3245,6 +3419,22 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/split-on-first": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/strict-uri-encode": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/strip-ansi": {
|
"node_modules/strip-ansi": {
|
||||||
"version": "6.0.1",
|
"version": "6.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||||
@ -3287,6 +3477,16 @@
|
|||||||
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
|
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/tiny-invariant": {
|
||||||
|
"version": "1.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
|
||||||
|
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="
|
||||||
|
},
|
||||||
|
"node_modules/tiny-warning": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
|
||||||
|
},
|
||||||
"node_modules/to-fast-properties": {
|
"node_modules/to-fast-properties": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
||||||
@ -3423,6 +3623,11 @@
|
|||||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/value-equal": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw=="
|
||||||
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "5.3.5",
|
"version": "5.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-5.3.5.tgz",
|
||||||
|
|||||||
@ -10,13 +10,15 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@react-buddy/ide-toolbox": "^2.4.0",
|
||||||
|
"axios": "^1.7.7",
|
||||||
"bootstrap": "^5.3.3",
|
"bootstrap": "^5.3.3",
|
||||||
"mobx": "^6.13.1",
|
"mobx": "^6.13.1",
|
||||||
"mobx-react": "^9.1.1",
|
"mobx-react": "^9.1.1",
|
||||||
|
"mobx-state-router": "^6.0.1",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-bootstrap": "^2.10.4",
|
"react-bootstrap": "^2.10.4",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1"
|
||||||
"@react-buddy/ide-toolbox": "^2.4.0"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/react": "^18.3.3",
|
"@types/react": "^18.3.3",
|
||||||
|
|||||||
@ -6,12 +6,12 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>ru.tubryansk</groupId>
|
<groupId>ru.tubryansk</groupId>
|
||||||
<artifactId>tdms</artifactId>
|
<artifactId>tdms</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<groupId>ru.tubryansk.tdms</groupId>
|
<groupId>ru.tubryansk.tdms</groupId>
|
||||||
<artifactId>web</artifactId>
|
<artifactId>web</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1</version>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
<name>TDMS :: WEB</name>
|
<name>TDMS :: WEB</name>
|
||||||
|
|||||||
20
web/src/Application.tsx
Normal file
20
web/src/Application.tsx
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import React from "react";
|
||||||
|
import ReactDOM from 'react-dom/client'
|
||||||
|
import './index.css'
|
||||||
|
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||||
|
import {RouterContext, RouterView} from "mobx-state-router";
|
||||||
|
import {initApp} from "./utils/init.ts";
|
||||||
|
import {MyRouterStore} from "./store/MyRouterStore.ts";
|
||||||
|
import { RootStoreContext } from "./store/RootStore.tsx";
|
||||||
|
|
||||||
|
const rootStore = initApp();
|
||||||
|
|
||||||
|
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||||
|
<React.StrictMode>
|
||||||
|
<RootStoreContext.Provider value={rootStore}>
|
||||||
|
<RouterContext.Provider value={rootStore.routerStore}>
|
||||||
|
<RouterView viewMap={MyRouterStore.makeViewMap()} />
|
||||||
|
</RouterContext.Provider>
|
||||||
|
</RootStoreContext.Provider>
|
||||||
|
</React.StrictMode>
|
||||||
|
);
|
||||||
@ -1,60 +0,0 @@
|
|||||||
import {GitHubLogo} from './Svgs.tsx';
|
|
||||||
import {Container, Nav, Navbar, NavDropdown} from "react-bootstrap";
|
|
||||||
|
|
||||||
const Header = () =>
|
|
||||||
<header>
|
|
||||||
<Navbar className="bg-body-tertiary" fixed="top">
|
|
||||||
<Container>
|
|
||||||
<Navbar.Brand className="" href="#">TDMS</Navbar.Brand>
|
|
||||||
<Nav>
|
|
||||||
<NavDropdown title="Группы">
|
|
||||||
<NavDropdown.Item href="#">Список</NavDropdown.Item>
|
|
||||||
<NavDropdown.Item href="#">Редактировать</NavDropdown.Item>
|
|
||||||
</NavDropdown>
|
|
||||||
</Nav>
|
|
||||||
|
|
||||||
<Nav className="ms-auto">
|
|
||||||
<Navbar.Text>Пользователь:</Navbar.Text>
|
|
||||||
<NavDropdown title="Фамилия И. О." id="basic-nav-dropdown">
|
|
||||||
<NavDropdown.Item href="#">Моя страница</NavDropdown.Item>
|
|
||||||
<NavDropdown.Divider/>
|
|
||||||
<NavDropdown.Item href="#">Выйти</NavDropdown.Item>
|
|
||||||
</NavDropdown>
|
|
||||||
</Nav>
|
|
||||||
</Container>
|
|
||||||
</Navbar>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
const Footer = () => {
|
|
||||||
return (
|
|
||||||
<footer>
|
|
||||||
<Navbar className="bg-body-tertiary">
|
|
||||||
<Container>
|
|
||||||
<Navbar.Text>Thesis Defence Management System ©</Navbar.Text>
|
|
||||||
<Nav>
|
|
||||||
<Nav.Link href="https://github.com/Velixeor/Thesis-Defense-Management-System">
|
|
||||||
<GitHubLogo width={32} height={32}/>
|
|
||||||
</Nav.Link>
|
|
||||||
</Nav>
|
|
||||||
</Container>
|
|
||||||
</Navbar>
|
|
||||||
</footer>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const Content = () =>
|
|
||||||
<article>
|
|
||||||
<div className="text-center mt-5">There you can see some dashboards in future :)</div>
|
|
||||||
</article>
|
|
||||||
|
|
||||||
function MainPage() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Header/>
|
|
||||||
<Content/>
|
|
||||||
<Footer/>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default MainPage;
|
|
||||||
19
web/src/components/Page/DefaultPage.tsx
Normal file
19
web/src/components/Page/DefaultPage.tsx
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import {Component, ReactNode} from "react";
|
||||||
|
import Header from "./Header.tsx";
|
||||||
|
import {Container} from "react-bootstrap";
|
||||||
|
import Footer from "./Footer.tsx";
|
||||||
|
|
||||||
|
export abstract class DefaultPage extends Component {
|
||||||
|
abstract get page(): ReactNode;
|
||||||
|
// declare context: ContextType<typeof RootStoreContext>
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return <>
|
||||||
|
<Header/>
|
||||||
|
<Container className={"mt-5 mb-5"}>
|
||||||
|
{this.page}
|
||||||
|
</Container>
|
||||||
|
<Footer/>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
}
|
||||||
7
web/src/components/Page/Error.tsx
Normal file
7
web/src/components/Page/Error.tsx
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import {DefaultPage} from "./DefaultPage.tsx";
|
||||||
|
|
||||||
|
export default class Error extends DefaultPage {
|
||||||
|
get page() {
|
||||||
|
return <h1>Error</h1>
|
||||||
|
}
|
||||||
|
}
|
||||||
21
web/src/components/Page/Footer.tsx
Normal file
21
web/src/components/Page/Footer.tsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import {Container, Nav, Navbar} from "react-bootstrap";
|
||||||
|
import {GitHubLogo} from "../../utils/svg.tsx";
|
||||||
|
|
||||||
|
const Footer = () => {
|
||||||
|
return (
|
||||||
|
<footer>
|
||||||
|
<Navbar className="bg-body-tertiary">
|
||||||
|
<Container>
|
||||||
|
<Navbar.Text>Thesis Defence Management System ©</Navbar.Text>
|
||||||
|
<Nav>
|
||||||
|
<Nav.Link href="https://github.com/Velixeor/Thesis-Defense-Management-System">
|
||||||
|
<GitHubLogo width={32} height={32}/>
|
||||||
|
</Nav.Link>
|
||||||
|
</Nav>
|
||||||
|
</Container>
|
||||||
|
</Navbar>
|
||||||
|
</footer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Footer;
|
||||||
49
web/src/components/Page/Header.tsx
Normal file
49
web/src/components/Page/Header.tsx
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import {Container, Nav, Navbar, NavDropdown} from "react-bootstrap";
|
||||||
|
import {FC} from "react";
|
||||||
|
import {RouterLink} from "mobx-state-router";
|
||||||
|
import {useRootStore} from "../../store/RootStore.tsx";
|
||||||
|
import {IAuthenticated} from "../../models/user.ts";
|
||||||
|
import {observer} from "mobx-react";
|
||||||
|
|
||||||
|
export const Header: FC = observer(() => {
|
||||||
|
const store = useRootStore();
|
||||||
|
const user = store.userStore.user;
|
||||||
|
|
||||||
|
return <header>
|
||||||
|
<Navbar className="bg-body-tertiary" fixed="top">
|
||||||
|
<Container>
|
||||||
|
<Navbar.Brand>
|
||||||
|
<Nav.Link as={RouterLink} routeName='root'>TDMS</Nav.Link>
|
||||||
|
</Navbar.Brand>
|
||||||
|
<Nav>
|
||||||
|
<NavDropdown title="Группы">
|
||||||
|
<NavDropdown.Item>Список</NavDropdown.Item>
|
||||||
|
<NavDropdown.Item>Редактировать</NavDropdown.Item>
|
||||||
|
</NavDropdown>
|
||||||
|
</Nav>
|
||||||
|
|
||||||
|
<Nav className="ms-auto">
|
||||||
|
{
|
||||||
|
user.authenticated &&
|
||||||
|
<>
|
||||||
|
<Navbar.Text>Пользователь:</Navbar.Text>
|
||||||
|
<NavDropdown
|
||||||
|
title={(user as IAuthenticated).fullName}>
|
||||||
|
<NavDropdown.Item>Моя страница</NavDropdown.Item>
|
||||||
|
<NavDropdown.Divider/>
|
||||||
|
<NavDropdown.Item onClick={() => store.userStore.logout()}>Выйти</NavDropdown.Item>
|
||||||
|
</NavDropdown>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
!user.authenticated &&
|
||||||
|
<Nav.Link as={RouterLink} routeName='login'>Войти</Nav.Link>
|
||||||
|
}
|
||||||
|
</Nav>
|
||||||
|
</Container>
|
||||||
|
</Navbar>
|
||||||
|
</header>
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Header;
|
||||||
7
web/src/components/Page/Root.tsx
Normal file
7
web/src/components/Page/Root.tsx
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import {DefaultPage} from "./DefaultPage.tsx";
|
||||||
|
|
||||||
|
export default class Root extends DefaultPage {
|
||||||
|
get page() {
|
||||||
|
return <h1>Home</h1>
|
||||||
|
}
|
||||||
|
}
|
||||||
7
web/src/components/Page/UserProfile.tsx
Normal file
7
web/src/components/Page/UserProfile.tsx
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import {DefaultPage} from "./DefaultPage.tsx";
|
||||||
|
|
||||||
|
export default class UserProfile extends DefaultPage {
|
||||||
|
get page() {
|
||||||
|
return <h1>User Profile</h1>
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,4 @@
|
|||||||
/* Reset css */
|
/* Reset css */
|
||||||
|
|
||||||
*, *::before, *::after {
|
*, *::before, *::after {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import ReactDOM from 'react-dom/client'
|
|
||||||
import MainPage from './components/MainPage.tsx'
|
|
||||||
import './index.css'
|
|
||||||
|
|
||||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
||||||
<React.StrictMode>
|
|
||||||
<MainPage/>
|
|
||||||
</React.StrictMode>,
|
|
||||||
)
|
|
||||||
13
web/src/models/user.ts
Normal file
13
web/src/models/user.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
export interface IAuthenticated {
|
||||||
|
authenticated: true,
|
||||||
|
login: string,
|
||||||
|
password: string,
|
||||||
|
fullName: string,
|
||||||
|
email: string,
|
||||||
|
phone: string,
|
||||||
|
createdAt: string,
|
||||||
|
updatedAt: string,
|
||||||
|
authorities: string[],
|
||||||
|
}
|
||||||
|
|
||||||
|
export declare type IUser = {authenticated: false} | IAuthenticated;
|
||||||
24
web/src/routes.tsx
Normal file
24
web/src/routes.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import UserProfile from "./components/Page/UserProfile.tsx";
|
||||||
|
import React from "react";
|
||||||
|
import Root from "./components/Page/Root.tsx";
|
||||||
|
import Error from "./components/Page/Error.tsx";
|
||||||
|
|
||||||
|
interface Route {
|
||||||
|
name: string;
|
||||||
|
pattern: string;
|
||||||
|
view: React.ReactElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const routes: Route[] = [{
|
||||||
|
name: 'root',
|
||||||
|
pattern: '/',
|
||||||
|
view: <Root/>,
|
||||||
|
}, {
|
||||||
|
name: 'profile',
|
||||||
|
pattern: '/profile',
|
||||||
|
view: <UserProfile/>,
|
||||||
|
}, {
|
||||||
|
name: 'error',
|
||||||
|
pattern: '/error',
|
||||||
|
view: <Error/>,
|
||||||
|
}];
|
||||||
3
web/src/services/UserService.ts
Normal file
3
web/src/services/UserService.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export default class UserService {
|
||||||
|
|
||||||
|
}
|
||||||
30
web/src/store/MyRouterStore.ts
Normal file
30
web/src/store/MyRouterStore.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import {browserHistory, createRouterState, HistoryAdapter, RouterStore} from "mobx-state-router";
|
||||||
|
import {routes} from "../routes.tsx";
|
||||||
|
import {RootStore} from "./RootStore.tsx";
|
||||||
|
|
||||||
|
|
||||||
|
export class MyRouterStore extends RouterStore {
|
||||||
|
constructor(rootStore: RootStore) {
|
||||||
|
super(MyRouterStore.makeRoutesMap(),
|
||||||
|
createRouterState('error', {notFound: true}),
|
||||||
|
{rootStore: rootStore});
|
||||||
|
}
|
||||||
|
|
||||||
|
static makeViewMap() {
|
||||||
|
return routes.reduce<any>((map, route) => {
|
||||||
|
map[route.name] = route.view;
|
||||||
|
return map;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
static makeRoutesMap() {
|
||||||
|
return routes.map(route => {
|
||||||
|
return {...route, view: undefined};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
const historyAdapter = new HistoryAdapter(this, browserHistory);
|
||||||
|
historyAdapter.observeRouterStateChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
31
web/src/store/RootStore.tsx
Normal file
31
web/src/store/RootStore.tsx
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import {UserStore} from "./UserStore.ts";
|
||||||
|
import {MyRouterStore} from "./MyRouterStore.ts";
|
||||||
|
import {createContext, useContext} from "react";
|
||||||
|
|
||||||
|
export class RootStore {
|
||||||
|
userStore = new UserStore(this);
|
||||||
|
routerStore = new MyRouterStore(this);
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this.userStore.init();
|
||||||
|
this.routerStore.init();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const RootStoreContext = createContext<RootStore | undefined>(
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
|
||||||
|
export function useRootStore(): RootStore {
|
||||||
|
const rootStore = useContext(RootStoreContext);
|
||||||
|
if (rootStore === undefined) {
|
||||||
|
throw new Error('useRootStore must be used within a RootStoreProvider');
|
||||||
|
}
|
||||||
|
|
||||||
|
return rootStore;
|
||||||
|
}
|
||||||
30
web/src/store/UserStore.ts
Normal file
30
web/src/store/UserStore.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import {get, post} from "../utils/request.tsx";
|
||||||
|
import {makeObservable, observable, runInAction} from "mobx";
|
||||||
|
import {RootStore} from "./RootStore.ts";
|
||||||
|
import {IUser} from "../models/user.ts";
|
||||||
|
|
||||||
|
export class UserStore {
|
||||||
|
rootStore: RootStore;
|
||||||
|
user: IUser = {authenticated: false};
|
||||||
|
|
||||||
|
constructor(rootStore: RootStore) {
|
||||||
|
makeObservable(this, {
|
||||||
|
user: observable,
|
||||||
|
});
|
||||||
|
this.rootStore = rootStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
get<IUser>('/user/current').then((response) => {
|
||||||
|
runInAction(() => {
|
||||||
|
this.user = response;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
logout() {
|
||||||
|
post('/user/logout').then(() => {
|
||||||
|
this.init();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
12
web/src/utils/init.ts
Normal file
12
web/src/utils/init.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import {configure} from "mobx";
|
||||||
|
import {RootStore} from "../store/RootStore.tsx";
|
||||||
|
|
||||||
|
|
||||||
|
export const initMobX = () => {
|
||||||
|
configure({enforceActions: 'observed'});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const initApp = () => {
|
||||||
|
initMobX();
|
||||||
|
return new RootStore();
|
||||||
|
}
|
||||||
32
web/src/utils/request.tsx
Normal file
32
web/src/utils/request.tsx
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import axios, {AxiosRequestConfig} from "axios";
|
||||||
|
|
||||||
|
export const apiUrl = "http://localhost:8080/api/v1/";
|
||||||
|
|
||||||
|
export const get = async <T extends unknown> (url: string, data?: T) => {
|
||||||
|
return await request({
|
||||||
|
url: url,
|
||||||
|
method: 'GET',
|
||||||
|
data: data,
|
||||||
|
}) as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const post = async <T extends unknown> (url: string, data?: T) => {
|
||||||
|
return request({
|
||||||
|
url: url,
|
||||||
|
method: 'POST',
|
||||||
|
data: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const request = async <T extends unknown> (config: AxiosRequestConfig<T>) => {
|
||||||
|
return new Promise<T>((resolve, reject) => {
|
||||||
|
console.debug(`${config.method} ${config.url} request: ${config.method === 'GET' ? JSON.stringify(config.params) : JSON.stringify(config.data)}`);
|
||||||
|
axios.request({...config, baseURL: apiUrl}).then((response) => {
|
||||||
|
console.debug(`${config.method} ${config.url} response: ${JSON.stringify(response.data)}`);
|
||||||
|
resolve(response.data);
|
||||||
|
}).catch((error) => {
|
||||||
|
console.error(`${config.method} ${config.url} error: ${error}`);
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
@ -22,7 +22,7 @@
|
|||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
"noUnusedParameters": true,
|
"noUnusedParameters": true,
|
||||||
"noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
"allowSyntheticDefaultImports": true
|
"allowSyntheticDefaultImports": true,
|
||||||
},
|
},
|
||||||
"include": ["src"]
|
"include": ["src"]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,5 +2,14 @@ import {defineConfig} from 'vite'
|
|||||||
import react from '@vitejs/plugin-react'
|
import react from '@vitejs/plugin-react'
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [react()],
|
esbuild: {
|
||||||
|
tsconfigRaw: {
|
||||||
|
compilerOptions: {
|
||||||
|
experimentalDecorators: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
react()
|
||||||
|
],
|
||||||
})
|
})
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user