feature/profile-diploma-topic #1
1
.gitignore
vendored
1
.gitignore
vendored
@ -30,3 +30,4 @@ build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
/logs/app.log
|
||||
|
||||
19
.mvn/wrapper/maven-wrapper.properties
vendored
19
.mvn/wrapper/maven-wrapper.properties
vendored
@ -1,19 +0,0 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
wrapperVersion=3.3.2
|
||||
distributionType=only-script
|
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.7/apache-maven-3.9.7-bin.zip
|
||||
@ -1,11 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Build & Run" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot">
|
||||
<option name="ACTIVE_PROFILES" value="dev" />
|
||||
<module name="server" />
|
||||
<option name="SPRING_BOOT_MAIN_CLASS" value="ru.tubryansk.tdms.TdmsApplication" />
|
||||
<method v="2">
|
||||
<option name="Maven.BeforeRunTask" enabled="true" file="$PROJECT_DIR$/pom.xml" goal="package -Dmaven.test.skip=true" />
|
||||
<option name="RunConfigurationTask" enabled="true" run_configuration_name="Start RDBMS" run_configuration_type="docker-deploy" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
@ -1,11 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Clean, Build & Run" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot">
|
||||
<option name="ACTIVE_PROFILES" value="dev" />
|
||||
<module name="server" />
|
||||
<option name="SPRING_BOOT_MAIN_CLASS" value="ru.tubryansk.tdms.TdmsApplication" />
|
||||
<method v="2">
|
||||
<option name="Maven.BeforeRunTask" enabled="true" file="$PROJECT_DIR$/pom.xml" goal="clean package -Dmaven.test.skip=true" />
|
||||
<option name="RunConfigurationTask" enabled="true" run_configuration_name="Start RDBMS" run_configuration_type="docker-deploy" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
@ -1,8 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Run" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot">
|
||||
<option name="ACTIVE_PROFILES" value="dev" />
|
||||
<module name="server" />
|
||||
<option name="SPRING_BOOT_MAIN_CLASS" value="ru.tubryansk.tdms.TdmsApplication" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@ -1,11 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Start RDBMS" type="docker-deploy" factoryName="docker-compose.yml" server-name="Docker">
|
||||
<deployment type="docker-compose.yml">
|
||||
<settings>
|
||||
<option name="envFilePath" value="" />
|
||||
<option name="sourceFilePath" value="server/docker-compose.yml" />
|
||||
</settings>
|
||||
</deployment>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@ -2,8 +2,8 @@ services:
|
||||
db:
|
||||
image: postgres:16.2-alpine3.19
|
||||
environment:
|
||||
- "POSTGRES_DB=db"
|
||||
- "POSTGRES_DB=tdms"
|
||||
- "POSTGRES_PASSWORD=root"
|
||||
- "POSTGRES_USER=root"
|
||||
ports:
|
||||
- "5432:5432"
|
||||
- "5400:5432"
|
||||
259
mvnw
vendored
259
mvnw
vendored
@ -1,259 +0,0 @@
|
||||
#!/bin/sh
|
||||
# ----------------------------------------------------------------------------
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Apache Maven Wrapper startup batch script, version 3.3.2
|
||||
#
|
||||
# Optional ENV vars
|
||||
# -----------------
|
||||
# JAVA_HOME - location of a JDK home dir, required when download maven via java source
|
||||
# MVNW_REPOURL - repo url base for downloading maven distribution
|
||||
# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
|
||||
# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
set -euf
|
||||
[ "${MVNW_VERBOSE-}" != debug ] || set -x
|
||||
|
||||
# OS specific support.
|
||||
native_path() { printf %s\\n "$1"; }
|
||||
case "$(uname)" in
|
||||
CYGWIN* | MINGW*)
|
||||
[ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
|
||||
native_path() { cygpath --path --windows "$1"; }
|
||||
;;
|
||||
esac
|
||||
|
||||
# set JAVACMD and JAVACCMD
|
||||
set_java_home() {
|
||||
# For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
|
||||
if [ -n "${JAVA_HOME-}" ]; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ]; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
JAVACCMD="$JAVA_HOME/jre/sh/javac"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
JAVACCMD="$JAVA_HOME/bin/javac"
|
||||
|
||||
if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
|
||||
echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
|
||||
echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
else
|
||||
JAVACMD="$(
|
||||
'set' +e
|
||||
'unset' -f command 2>/dev/null
|
||||
'command' -v java
|
||||
)" || :
|
||||
JAVACCMD="$(
|
||||
'set' +e
|
||||
'unset' -f command 2>/dev/null
|
||||
'command' -v javac
|
||||
)" || :
|
||||
|
||||
if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
|
||||
echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# hash string like Java String::hashCode
|
||||
hash_string() {
|
||||
str="${1:-}" h=0
|
||||
while [ -n "$str" ]; do
|
||||
char="${str%"${str#?}"}"
|
||||
h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
|
||||
str="${str#?}"
|
||||
done
|
||||
printf %x\\n $h
|
||||
}
|
||||
|
||||
verbose() { :; }
|
||||
[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
|
||||
|
||||
die() {
|
||||
printf %s\\n "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
trim() {
|
||||
# MWRAPPER-139:
|
||||
# Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
|
||||
# Needed for removing poorly interpreted newline sequences when running in more
|
||||
# exotic environments such as mingw bash on Windows.
|
||||
printf "%s" "${1}" | tr -d '[:space:]'
|
||||
}
|
||||
|
||||
# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
|
||||
while IFS="=" read -r key value; do
|
||||
case "${key-}" in
|
||||
distributionUrl) distributionUrl=$(trim "${value-}") ;;
|
||||
distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
|
||||
esac
|
||||
done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties"
|
||||
[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties"
|
||||
|
||||
case "${distributionUrl##*/}" in
|
||||
maven-mvnd-*bin.*)
|
||||
MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
|
||||
case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
|
||||
*AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
|
||||
:Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
|
||||
:Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
|
||||
:Linux*x86_64*) distributionPlatform=linux-amd64 ;;
|
||||
*)
|
||||
echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2
|
||||
distributionPlatform=linux-amd64
|
||||
;;
|
||||
esac
|
||||
distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
|
||||
;;
|
||||
maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
|
||||
*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
|
||||
esac
|
||||
|
||||
# apply MVNW_REPOURL and calculate MAVEN_HOME
|
||||
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
|
||||
[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
|
||||
distributionUrlName="${distributionUrl##*/}"
|
||||
distributionUrlNameMain="${distributionUrlName%.*}"
|
||||
distributionUrlNameMain="${distributionUrlNameMain%-bin}"
|
||||
MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
|
||||
MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
|
||||
|
||||
exec_maven() {
|
||||
unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
|
||||
exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD"
|
||||
}
|
||||
|
||||
if [ -d "$MAVEN_HOME" ]; then
|
||||
verbose "found existing MAVEN_HOME at $MAVEN_HOME"
|
||||
exec_maven "$@"
|
||||
fi
|
||||
|
||||
case "${distributionUrl-}" in
|
||||
*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
|
||||
*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
|
||||
esac
|
||||
|
||||
# prepare tmp dir
|
||||
if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
|
||||
clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
|
||||
trap clean HUP INT TERM EXIT
|
||||
else
|
||||
die "cannot create temp dir"
|
||||
fi
|
||||
|
||||
mkdir -p -- "${MAVEN_HOME%/*}"
|
||||
|
||||
# Download and Install Apache Maven
|
||||
verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
|
||||
verbose "Downloading from: $distributionUrl"
|
||||
verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
|
||||
|
||||
# select .zip or .tar.gz
|
||||
if ! command -v unzip >/dev/null; then
|
||||
distributionUrl="${distributionUrl%.zip}.tar.gz"
|
||||
distributionUrlName="${distributionUrl##*/}"
|
||||
fi
|
||||
|
||||
# verbose opt
|
||||
__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''
|
||||
[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v
|
||||
|
||||
# normalize http auth
|
||||
case "${MVNW_PASSWORD:+has-password}" in
|
||||
'') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
|
||||
has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;
|
||||
esac
|
||||
|
||||
if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
|
||||
verbose "Found wget ... using wget"
|
||||
wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl"
|
||||
elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
|
||||
verbose "Found curl ... using curl"
|
||||
curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl"
|
||||
elif set_java_home; then
|
||||
verbose "Falling back to use Java to download"
|
||||
javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
|
||||
targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
|
||||
cat >"$javaSource" <<-END
|
||||
public class Downloader extends java.net.Authenticator
|
||||
{
|
||||
protected java.net.PasswordAuthentication getPasswordAuthentication()
|
||||
{
|
||||
return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
|
||||
}
|
||||
public static void main( String[] args ) throws Exception
|
||||
{
|
||||
setDefault( new Downloader() );
|
||||
java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
|
||||
}
|
||||
}
|
||||
END
|
||||
# For Cygwin/MinGW, switch paths to Windows format before running javac and java
|
||||
verbose " - Compiling Downloader.java ..."
|
||||
"$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java"
|
||||
verbose " - Running Downloader.java ..."
|
||||
"$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")"
|
||||
fi
|
||||
|
||||
# If specified, validate the SHA-256 sum of the Maven distribution zip file
|
||||
if [ -n "${distributionSha256Sum-}" ]; then
|
||||
distributionSha256Result=false
|
||||
if [ "$MVN_CMD" = mvnd.sh ]; then
|
||||
echo "Checksum validation is not supported for maven-mvnd." >&2
|
||||
echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
|
||||
exit 1
|
||||
elif command -v sha256sum >/dev/null; then
|
||||
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then
|
||||
distributionSha256Result=true
|
||||
fi
|
||||
elif command -v shasum >/dev/null; then
|
||||
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
|
||||
distributionSha256Result=true
|
||||
fi
|
||||
else
|
||||
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
|
||||
echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ $distributionSha256Result = false ]; then
|
||||
echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
|
||||
echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# unzip and move
|
||||
if command -v unzip >/dev/null; then
|
||||
unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
|
||||
else
|
||||
tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
|
||||
fi
|
||||
printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url"
|
||||
mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
|
||||
|
||||
clean || :
|
||||
exec_maven "$@"
|
||||
149
mvnw.cmd
vendored
149
mvnw.cmd
vendored
@ -1,149 +0,0 @@
|
||||
<# : batch portion
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||
@REM or more contributor license agreements. See the NOTICE file
|
||||
@REM distributed with this work for additional information
|
||||
@REM regarding copyright ownership. The ASF licenses this file
|
||||
@REM to you under the Apache License, Version 2.0 (the
|
||||
@REM "License"); you may not use this file except in compliance
|
||||
@REM with the License. You may obtain a copy of the License at
|
||||
@REM
|
||||
@REM https://www.apache.org/licenses/LICENSE-2.0
|
||||
@REM
|
||||
@REM Unless required by applicable law or agreed to in writing,
|
||||
@REM software distributed under the License is distributed on an
|
||||
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
@REM KIND, either express or implied. See the License for the
|
||||
@REM specific language governing permissions and limitations
|
||||
@REM under the License.
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Apache Maven Wrapper startup batch script, version 3.3.2
|
||||
@REM
|
||||
@REM Optional ENV vars
|
||||
@REM MVNW_REPOURL - repo url base for downloading maven distribution
|
||||
@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
|
||||
@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
|
||||
@SET __MVNW_CMD__=
|
||||
@SET __MVNW_ERROR__=
|
||||
@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
|
||||
@SET PSModulePath=
|
||||
@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)
|
||||
)
|
||||
@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
|
||||
@SET __MVNW_PSMODULEP_SAVE=
|
||||
@SET __MVNW_ARG0_NAME__=
|
||||
@SET MVNW_USERNAME=
|
||||
@SET MVNW_PASSWORD=
|
||||
@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*)
|
||||
@echo Cannot start maven from wrapper >&2 && exit /b 1
|
||||
@GOTO :EOF
|
||||
: end batch / begin powershell #>
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
if ($env:MVNW_VERBOSE -eq "true") {
|
||||
$VerbosePreference = "Continue"
|
||||
}
|
||||
|
||||
# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
|
||||
$distributionUrl = (Get-PageContent -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
|
||||
if (!$distributionUrl) {
|
||||
Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
|
||||
}
|
||||
|
||||
switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
|
||||
"maven-mvnd-*" {
|
||||
$USE_MVND = $true
|
||||
$distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
|
||||
$MVN_CMD = "mvnd.cmd"
|
||||
break
|
||||
}
|
||||
default {
|
||||
$USE_MVND = $false
|
||||
$MVN_CMD = $script -replace '^mvnw','mvn'
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
# apply MVNW_REPOURL and calculate MAVEN_HOME
|
||||
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
|
||||
if ($env:MVNW_REPOURL) {
|
||||
$MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" }
|
||||
$distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')"
|
||||
}
|
||||
$distributionUrlName = $distributionUrl -replace '^.*/',''
|
||||
$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
|
||||
$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain"
|
||||
if ($env:MAVEN_USER_HOME) {
|
||||
$MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain"
|
||||
}
|
||||
$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
|
||||
$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
|
||||
|
||||
if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
|
||||
Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
|
||||
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
|
||||
exit $?
|
||||
}
|
||||
|
||||
if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
|
||||
Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
|
||||
}
|
||||
|
||||
# prepare tmp dir
|
||||
$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
|
||||
$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
|
||||
$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
|
||||
trap {
|
||||
if ($TMP_DOWNLOAD_DIR.Exists) {
|
||||
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
|
||||
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
|
||||
}
|
||||
}
|
||||
|
||||
New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
|
||||
|
||||
# Download and Install Apache Maven
|
||||
Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
|
||||
Write-Verbose "Downloading from: $distributionUrl"
|
||||
Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
|
||||
|
||||
$webclient = New-Object System.Net.WebClient
|
||||
if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
|
||||
$webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
|
||||
}
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||
$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
|
||||
|
||||
# If specified, validate the SHA-256 sum of the Maven distribution zip file
|
||||
$distributionSha256Sum = (Get-PageContent -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
|
||||
if ($distributionSha256Sum) {
|
||||
if ($USE_MVND) {
|
||||
Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
|
||||
}
|
||||
Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
|
||||
if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
|
||||
Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
|
||||
}
|
||||
}
|
||||
|
||||
# unzip and move
|
||||
Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
|
||||
Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null
|
||||
try {
|
||||
Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
|
||||
} catch {
|
||||
if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
|
||||
Write-Error "fail to move MAVEN_HOME"
|
||||
}
|
||||
} finally {
|
||||
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
|
||||
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
|
||||
}
|
||||
|
||||
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
|
||||
1
server/.gitignore
vendored
1
server/.gitignore
vendored
@ -1 +1,2 @@
|
||||
/target
|
||||
/logs/app.log
|
||||
|
||||
@ -88,6 +88,16 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.14.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-collections4</artifactId>
|
||||
<version>4.4</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@ -11,8 +11,6 @@ import org.springframework.context.ConfigurableApplicationContext;
|
||||
@Slf4j
|
||||
public class TdmsApplication {
|
||||
public static void main(String[] args) {
|
||||
ConfigurableApplicationContext context = SpringApplication.run(TdmsApplication.class, args);
|
||||
String staticLocation = context.getEnvironment().getProperty("spring.web.resources.static-locations");
|
||||
log.info("Static location: {}", staticLocation);
|
||||
SpringApplication.run(TdmsApplication.class, args);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,24 +1,22 @@
|
||||
package ru.tubryansk.tdms.config;
|
||||
|
||||
|
||||
import jakarta.servlet.http.HttpSessionEvent;
|
||||
import jakarta.servlet.http.HttpSessionListener;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.ProviderManager;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
@ -26,78 +24,88 @@ 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 java.time.Duration;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@Configuration
|
||||
@Slf4j
|
||||
public class SecurityConfiguration {
|
||||
@Bean
|
||||
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity, AuthenticationManager authenticationManager) throws Exception {
|
||||
public SecurityFilterChain securityFilterChain(
|
||||
HttpSecurity httpSecurity,
|
||||
AuthenticationManager authenticationManager,
|
||||
@Qualifier("corsConfig") CorsConfigurationSource cors
|
||||
) throws Exception {
|
||||
return httpSecurity
|
||||
.authorizeHttpRequests(this::configureHttpAuthorization)
|
||||
.csrf(AbstractHttpConfigurer::disable)
|
||||
.cors(a -> a.configurationSource(corsConfiguration()))
|
||||
.authenticationManager(authenticationManager)
|
||||
.sessionManagement(this::configureSessionManagement)
|
||||
.build();
|
||||
.authorizeHttpRequests(this::configureHttpAuthorization)
|
||||
.csrf(AbstractHttpConfigurer::disable) /* todo: настроить csrf */
|
||||
.cors(a -> a.configurationSource(cors))
|
||||
.authenticationManager(authenticationManager)
|
||||
.sessionManagement(cfg -> {
|
||||
cfg.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
|
||||
cfg.maximumSessions(1);
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CorsConfigurationSource corsConfiguration() {
|
||||
@Qualifier("corsConfig")
|
||||
public CorsConfigurationSource corsConfigurationProd(
|
||||
@Value("${application.domain}") String domain,
|
||||
@Value("${application.port}") String port,
|
||||
@Value("${application.protocol}") String protocol,
|
||||
Environment environment
|
||||
) {
|
||||
return request -> {
|
||||
String url = StringUtils.join(protocol, "://", domain, ":", port);
|
||||
CorsConfiguration corsConfiguration = new CorsConfiguration();
|
||||
corsConfiguration.applyPermitDefaultValues();
|
||||
corsConfiguration.addAllowedMethod("DELETE");
|
||||
corsConfiguration.addAllowedMethod("PUT");
|
||||
corsConfiguration.addAllowedMethod("PATCH");
|
||||
corsConfiguration.setMaxAge(Duration.ofDays(1));
|
||||
corsConfiguration.addAllowedOrigin(url);
|
||||
if (environment.matchesProfiles("dev")) {
|
||||
corsConfiguration.addAllowedOrigin("http://localhost:8081");
|
||||
}
|
||||
|
||||
corsConfiguration.setAllowedMethods(List.of(HttpMethod.GET.name(), HttpMethod.POST.name(), HttpMethod.OPTIONS.name()));
|
||||
corsConfiguration.setAllowedHeaders(List.of("Authorization", "Content-Type"));
|
||||
corsConfiguration.setAllowCredentials(true);
|
||||
return corsConfiguration;
|
||||
};
|
||||
}
|
||||
|
||||
private void configureHttpAuthorization(AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry httpAuthorization) {
|
||||
/* API ROUTES */
|
||||
httpAuthorization.requestMatchers("/api/v1/diploma-topic/**").permitAll();
|
||||
httpAuthorization.requestMatchers("/api/v1/user/**").permitAll();
|
||||
httpAuthorization.requestMatchers("/api/**").denyAll();
|
||||
/* STATIC ROUTES */
|
||||
httpAuthorization.requestMatchers("/**").permitAll();
|
||||
/* OTHER */
|
||||
httpAuthorization.anyRequest().denyAll();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AuthenticationManager authenticationManager(UserDetailsService userDetailsService) {
|
||||
return new ProviderManager(authenticationProvider(userDetailsService));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
|
||||
}
|
||||
|
||||
private void configureHttpAuthorization(AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry httpAuthorization) {
|
||||
/* SysInfoController */
|
||||
httpAuthorization.requestMatchers("/api/v1/sysinfo/**").permitAll();
|
||||
/* UserController */
|
||||
httpAuthorization.requestMatchers("/api/v1/user/logout").authenticated();
|
||||
httpAuthorization.requestMatchers("/api/v1/user/login").anonymous();
|
||||
httpAuthorization.requestMatchers("/api/v1/user/current").permitAll();
|
||||
httpAuthorization.requestMatchers("/api/v1/user/get-all").hasAuthority("ROLE_ADMINISTRATOR");
|
||||
httpAuthorization.requestMatchers("/api/v1/user/register").hasAuthority("ROLE_ADMINISTRATOR");
|
||||
httpAuthorization.requestMatchers("/api/v1/user/validate-registration").hasAuthority("ROLE_ADMINISTRATOR");
|
||||
/* StudentController */
|
||||
httpAuthorization.requestMatchers("/api/v1/student/current").permitAll();
|
||||
/* GroupController */
|
||||
httpAuthorization.requestMatchers("/api/v1/group/get-all").permitAll();
|
||||
/* deny all other api requests */
|
||||
httpAuthorization.requestMatchers("/api/**").denyAll();
|
||||
/* since api already blocked, all other requests are static resources */
|
||||
httpAuthorization.requestMatchers("/**").permitAll();
|
||||
}
|
||||
|
||||
private AuthenticationProvider authenticationProvider(UserDetailsService userDetailsService) {
|
||||
DaoAuthenticationProvider provider = new DaoAuthenticationProvider(passwordEncoder());
|
||||
provider.setUserDetailsService(userDetailsService);
|
||||
return provider;
|
||||
}
|
||||
|
||||
private PasswordEncoder passwordEncoder() {
|
||||
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public HttpSessionListener autoAuthenticateUnderAdmin(AuthenticationManager authenticationManager) {
|
||||
return new HttpSessionListener() {
|
||||
@Override
|
||||
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();
|
||||
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken("izrailev_v_ya_1", "1");
|
||||
Authentication authenticated = authenticationManager.authenticate(authentication);
|
||||
context.setAuthentication(authenticated);
|
||||
SecurityContextHolder.setContext(context);
|
||||
se.getSession().setAttribute(SPRING_SECURITY_CONTEXT_KEY, context);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// todo: remove when login/logout is implemented, since we do not need automatically created session with no authentication
|
||||
private void configureSessionManagement(SessionManagementConfigurer<HttpSecurity> sessionManagement) {
|
||||
sessionManagement.sessionCreationPolicy(SessionCreationPolicy.ALWAYS);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,33 +1,13 @@
|
||||
package ru.tubryansk.tdms.controller;
|
||||
|
||||
|
||||
import jakarta.validation.constraints.PositiveOrZero;
|
||||
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.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import ru.tubryansk.tdms.dto.DiplomaTopicDTO;
|
||||
import ru.tubryansk.tdms.service.DiplomaTopicService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/diploma-topic/")
|
||||
@Validated
|
||||
public class DiplomaTopicController {
|
||||
@Autowired
|
||||
private DiplomaTopicService diplomaTopicService;
|
||||
|
||||
@GetMapping("/get-all")
|
||||
public List<DiplomaTopicDTO> getAll() {
|
||||
return diplomaTopicService.getAll();
|
||||
}
|
||||
|
||||
@GetMapping("/get-by-id/{id:[\\-+]?\\d+}")
|
||||
public DiplomaTopicDTO getById(@PathVariable @PositiveOrZero Integer id) {
|
||||
return diplomaTopicService.getById(id);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,20 @@
|
||||
package ru.tubryansk.tdms.controller;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import ru.tubryansk.tdms.controller.payload.GroupDTO;
|
||||
import ru.tubryansk.tdms.service.GroupService;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@RestController("/api/v1/group/")
|
||||
public class GroupController {
|
||||
@Autowired
|
||||
private GroupService groupService;
|
||||
|
||||
@GetMapping("/get-all-groups")
|
||||
public Collection<GroupDTO> getAllGroups() {
|
||||
return groupService.getAllGroups();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package ru.tubryansk.tdms.controller;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import ru.tubryansk.tdms.controller.payload.StudentDTO;
|
||||
import ru.tubryansk.tdms.service.StudentService;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/student/")
|
||||
public class StudentController {
|
||||
@Autowired
|
||||
private StudentService studentService;
|
||||
|
||||
@GetMapping("/current")
|
||||
public StudentDTO getCurrentStudent() {
|
||||
return studentService.getCallerStudentDTO();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package ru.tubryansk.tdms.controller;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import ru.tubryansk.tdms.service.SysInfoService;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/sysinfo")
|
||||
public class SysInfoController {
|
||||
@Autowired
|
||||
private SysInfoService sysInfoService;
|
||||
|
||||
@SneakyThrows
|
||||
@GetMapping("/version")
|
||||
public String getVersion() {
|
||||
return sysInfoService.getVersion();
|
||||
}
|
||||
}
|
||||
@ -1,32 +1,51 @@
|
||||
package ru.tubryansk.tdms.controller;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
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 org.springframework.web.bind.annotation.*;
|
||||
import ru.tubryansk.tdms.controller.payload.LoginDTO;
|
||||
import ru.tubryansk.tdms.controller.payload.RegistrationDTO;
|
||||
import ru.tubryansk.tdms.controller.payload.UserDTO;
|
||||
import ru.tubryansk.tdms.service.AuthenticationService;
|
||||
import ru.tubryansk.tdms.service.CallerService;
|
||||
import ru.tubryansk.tdms.service.UserService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@Validated
|
||||
@RequestMapping("/api/v1/user")
|
||||
@Slf4j
|
||||
public class UserController {
|
||||
@Autowired
|
||||
private AuthenticationService authenticationService;
|
||||
@Autowired
|
||||
private CallerService callerService;
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
@GetMapping("/current")
|
||||
public UserDTO getCurrentUser() {
|
||||
User principal = userService.getCallerPrincipal();
|
||||
return principal != null ? UserDTO.from(principal, true) : UserDTO.fromUnauthenticated();
|
||||
return callerService.getCallerUserDTO();
|
||||
}
|
||||
|
||||
@PostMapping("/logout")
|
||||
public void logout() {
|
||||
userService.logout();
|
||||
authenticationService.logout();
|
||||
}
|
||||
|
||||
@PostMapping("/login")
|
||||
public void login(@RequestBody @Valid LoginDTO loginDTO) {
|
||||
authenticationService.login(loginDTO.getUsername(), loginDTO.getPassword());
|
||||
}
|
||||
|
||||
@PostMapping("/register")
|
||||
public void post(@RequestBody @Valid RegistrationDTO registrationDTO) {
|
||||
userService.registerUser(registrationDTO);
|
||||
}
|
||||
|
||||
@GetMapping("/get-all")
|
||||
public List<UserDTO> getAllUsers() {
|
||||
return userService.getAllUsers();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package ru.tubryansk.tdms.dto;
|
||||
package ru.tubryansk.tdms.controller.payload;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@ -8,11 +8,11 @@ public record ErrorResponse(String message, ErrorCode errorCode) {
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
public enum ErrorCode {
|
||||
BAD_REQUEST(HttpStatus.BAD_REQUEST),
|
||||
BUSINESS_ERROR(HttpStatus.BAD_REQUEST),
|
||||
VALIDATION_ERROR(HttpStatus.BAD_REQUEST),
|
||||
INTERNAL_ERROR(HttpStatus.INTERNAL_SERVER_ERROR),
|
||||
NOT_FOUND(HttpStatus.NOT_FOUND),
|
||||
ACCESS_DENIED(HttpStatus.FORBIDDEN)
|
||||
ACCESS_DENIED(HttpStatus.FORBIDDEN),
|
||||
;
|
||||
|
||||
private final HttpStatus httpStatus;
|
||||
@ -0,0 +1,14 @@
|
||||
package ru.tubryansk.tdms.controller.payload;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
public class GroupDTO {
|
||||
private String name;
|
||||
private String principalName;
|
||||
private Boolean isMePrincipal;
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
package ru.tubryansk.tdms.controller.payload;
|
||||
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class LoginDTO {
|
||||
@NotEmpty(message = "Логин не может быть пустым")
|
||||
private String username;
|
||||
@NotEmpty(message = "Пароль не может быть пустым")
|
||||
private String password;
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
package ru.tubryansk.tdms.controller.payload;
|
||||
|
||||
import jakarta.validation.constraints.*;
|
||||
import lombok.Getter;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
@Getter
|
||||
public class RegistrationDTO {
|
||||
@NotEmpty(message = "Логин не может быть пустым")
|
||||
@Pattern(regexp = "^[a-zA-Z0-9_]+$", message = "Логин должен содержать только латинские буквы, цифры и знак подчеркивания")
|
||||
@Size(min = 5, message = "Логин должен содержать минимум 5 символов")
|
||||
@Size(max = 32, message = "Логин должен содержать максимум 32 символов")
|
||||
private String login;
|
||||
@NotEmpty(message = "Пароль не может быть пустым")
|
||||
@Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[a-zA-Z\\d]{8,}$", message = "Пароль должен содержать хотя бы одну цифру, одну заглавную и одну строчную букву, минимум 8 символов")
|
||||
private String password;
|
||||
@NotEmpty(message = "Имя не может быть пустым")
|
||||
@Length(min = 3, message = "Имя должно содержать минимум 3 символа")
|
||||
@Pattern(regexp = "^[a-zA-Zа-яА-ЯёЁ\\s]+$", message = "Имя должно содержать только буквы английского или русского алфавита и пробелы")
|
||||
private String fullName;
|
||||
@NotNull(message = "Почта не может быть пустой")
|
||||
@Email(message = "Почта должна быть валидным адресом электронной почты")
|
||||
private String email;
|
||||
@NotNull(message = "Номер телефона не может быть пустым")
|
||||
@Pattern(regexp = "^\\+[1-9]\\d{6,14}$", message = "Номер телефона должен начинаться с '+' и содержать от 7 до 15 цифр")
|
||||
private String numberPhone;
|
||||
private StudentRegistrationDTO studentData;
|
||||
|
||||
@Getter
|
||||
public static class StudentRegistrationDTO {
|
||||
@NotNull(message = "Группа не может быть пустой")
|
||||
private Long groupId;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package ru.tubryansk.tdms.controller.payload;
|
||||
|
||||
|
||||
import ru.tubryansk.tdms.entity.Role;
|
||||
import ru.tubryansk.tdms.entity.User;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public record RoleDTO(String name, String authority) {
|
||||
|
||||
public static RoleDTO from(Role role) {
|
||||
return new RoleDTO(role.getName(), role.getAuthority());
|
||||
}
|
||||
|
||||
public static List<RoleDTO> from(User user) {
|
||||
return user.getRoles().stream().map(RoleDTO::from).toList();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,50 @@
|
||||
package ru.tubryansk.tdms.controller.payload;
|
||||
|
||||
|
||||
import lombok.Data;
|
||||
import ru.tubryansk.tdms.entity.Student;
|
||||
|
||||
|
||||
@Data
|
||||
public class StudentDTO {
|
||||
// private Boolean form;
|
||||
// private Integer protectionOrder;
|
||||
// private String magistracy;
|
||||
// private Boolean digitalFormatPresent;
|
||||
// private Integer markComment;
|
||||
// private Integer markPractice;
|
||||
// private String predefenceComment;
|
||||
// private String normalControl;
|
||||
// private Integer antiPlagiarism;
|
||||
// private String note;
|
||||
// private Boolean recordBookReturned;
|
||||
// private String work;
|
||||
// private UserDTO user;
|
||||
// private String diplomaTopic;
|
||||
// private UserDTO mentorUser;
|
||||
// private GroupDTO group;
|
||||
|
||||
public static StudentDTO from(Student student) {
|
||||
StudentDTO studentDTO = new StudentDTO();
|
||||
// studentDTO.setForm(student.getForm());
|
||||
// return studentDTO;
|
||||
// student.getForm(),
|
||||
// student.getProtectionOrder(),
|
||||
// student.getMagistracy(),
|
||||
// student.getDigitalFormatPresent(),
|
||||
// student.getMarkComment(),
|
||||
// student.getMarkPractice(),
|
||||
// student.getPredefenceComment(),
|
||||
// student.getNormalControl(),
|
||||
// student.getAntiPlagiarism(),
|
||||
// student.getNote(),
|
||||
// student.getRecordBookReturned(),
|
||||
// student.getWork(),
|
||||
// UserDTO.from(student.getUser()),
|
||||
// student.getDiplomaTopic().getName(),
|
||||
// UserDTO.from(student.getMentorUser()),
|
||||
// GroupDTO.from(student.getGroup())
|
||||
// );
|
||||
return studentDTO;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
package ru.tubryansk.tdms.controller.payload;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import lombok.Builder;
|
||||
import ru.tubryansk.tdms.entity.User;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@Builder
|
||||
@JsonInclude(JsonInclude.Include.NON_ABSENT)
|
||||
public record UserDTO(
|
||||
boolean authenticated,
|
||||
String login,
|
||||
String fullName,
|
||||
String email,
|
||||
String phone,
|
||||
ZonedDateTime createdAt,
|
||||
ZonedDateTime updatedAt,
|
||||
List<RoleDTO> authorities) {
|
||||
|
||||
public static UserDTO unauthenticated() {
|
||||
return UserDTO.builder()
|
||||
.authenticated(false)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static UserDTO from(User user) {
|
||||
return UserDTO.builder()
|
||||
.authenticated(true)
|
||||
.login(user.getLogin())
|
||||
.fullName(user.getFullName())
|
||||
.email(user.getEmail())
|
||||
.phone(user.getNumberPhone())
|
||||
.createdAt(user.getCreatedAt())
|
||||
.updatedAt(user.getUpdatedAt())
|
||||
.authorities(RoleDTO.from(user))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
package ru.tubryansk.tdms.dto;
|
||||
|
||||
|
||||
import lombok.Builder;
|
||||
import ru.tubryansk.tdms.entity.DiplomaTopic;
|
||||
|
||||
|
||||
@Builder
|
||||
public record DiplomaTopicDTO(Integer id, String name) {
|
||||
public static DiplomaTopicDTO fromEntity(DiplomaTopic diplomaTopic) {
|
||||
return DiplomaTopicDTO.builder()
|
||||
.id(diplomaTopic.getId())
|
||||
.name(diplomaTopic.getName())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
package ru.tubryansk.tdms.dto;
|
||||
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class RoleDTO {
|
||||
private Integer id;
|
||||
private String name;
|
||||
}
|
||||
@ -1,30 +0,0 @@
|
||||
package ru.tubryansk.tdms.dto;
|
||||
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class StudentDTO {
|
||||
private Integer id;
|
||||
private Boolean form;
|
||||
private Integer protectionOrder;
|
||||
private String magistracy;
|
||||
private Boolean digitalFormatPresent;
|
||||
private Integer markComment;
|
||||
private Integer markPractice;
|
||||
private String predefenceComment;
|
||||
private String normalControl;
|
||||
private Integer antiPlagiarism;
|
||||
private String note;
|
||||
private Boolean recordBookReturned;
|
||||
private String work;
|
||||
private Integer userId;
|
||||
private Integer diplomaTopicId;
|
||||
private Integer mentorUserId;
|
||||
private Integer groupId;
|
||||
}
|
||||
@ -1,43 +0,0 @@
|
||||
package ru.tubryansk.tdms.dto;
|
||||
|
||||
|
||||
import lombok.Builder;
|
||||
import ru.tubryansk.tdms.entity.Role;
|
||||
import ru.tubryansk.tdms.entity.User;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@Builder
|
||||
public record UserDTO(
|
||||
boolean authenticated,
|
||||
String login,
|
||||
String password,
|
||||
String fullName,
|
||||
String email,
|
||||
String phoneNumber,
|
||||
ZonedDateTime createdAt,
|
||||
ZonedDateTime updatedAt,
|
||||
List<String> authorities) {
|
||||
|
||||
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.getCreatedAt())
|
||||
.updatedAt(user.getUpdatedAt())
|
||||
.authorities(user.getRoles().stream().map(Role::getAuthority).toList())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@ -2,23 +2,22 @@ package ru.tubryansk.tdms.entity;
|
||||
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
|
||||
@Entity
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Table(name = "diploma_topic", schema = "vkr")
|
||||
@ToString
|
||||
@Entity
|
||||
@Table(name = "diploma_topic")
|
||||
public class DiplomaTopic {
|
||||
@Id
|
||||
@Column(name = "id")
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)//Макс из-за SEQUENCE прога лежит, если хочешь, можешь менять я не понял что не нравиться при сборке
|
||||
private Integer id;
|
||||
@Column(name = "name", nullable = false)
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
@Column(name = "name")
|
||||
private String name;
|
||||
}
|
||||
|
||||
|
||||
@ -2,26 +2,24 @@ package ru.tubryansk.tdms.entity;
|
||||
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
|
||||
@Entity
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Table(name = "group", schema = "vkr")
|
||||
@ToString
|
||||
@Entity
|
||||
@Table(name = "group")
|
||||
public class Group {
|
||||
@Id
|
||||
@Column(name = "id")
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Integer id;
|
||||
@Column(name = "name", nullable = false)
|
||||
private Long id;
|
||||
@Column(name = "name")
|
||||
private String name;
|
||||
@ManyToOne()
|
||||
@JoinColumn(name = "principal_user_id", nullable = false)
|
||||
private User principalUser;
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "curator_user_id")
|
||||
private User groupCurator;
|
||||
}
|
||||
|
||||
@ -1,28 +1,25 @@
|
||||
package ru.tubryansk.tdms.entity;
|
||||
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
|
||||
|
||||
@Entity
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Table(name = "role", schema = "vkr")
|
||||
@ToString
|
||||
@Entity
|
||||
@Table(name = "`role`")
|
||||
public class Role implements GrantedAuthority {
|
||||
@Id
|
||||
@Column(name = "id")
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Integer id;
|
||||
@Column(name = "name", nullable = false)
|
||||
private Long id;
|
||||
@Column(name = "name")
|
||||
private String name;
|
||||
@Column(name = "authority", nullable = false)
|
||||
@Column(name = "authority")
|
||||
private String authority;
|
||||
}
|
||||
|
||||
@ -2,22 +2,26 @@ package ru.tubryansk.tdms.entity;
|
||||
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
|
||||
@Data
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
@Entity
|
||||
@Table(name = "student", schema = "vkr")
|
||||
@Table(name = "student")
|
||||
public class Student {
|
||||
@Id
|
||||
@Column(name = "id")
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Integer id;
|
||||
private Long id;
|
||||
@Column(name = "form")
|
||||
private Boolean form;
|
||||
@Column(name = "protection_order" , nullable = false)
|
||||
@Column(name = "protection_order")
|
||||
private Integer protectionOrder;
|
||||
@Column(name = "magistracy" )
|
||||
@Column(name = "magistracy")
|
||||
private String magistracy;
|
||||
@Column(name = "digital_format_present")
|
||||
private Boolean digitalFormatPresent;
|
||||
@ -36,18 +40,17 @@ public class Student {
|
||||
@Column(name = "record_book_returned")
|
||||
private Boolean recordBookReturned;
|
||||
@Column(name = "work")
|
||||
private String work;
|
||||
private String work;
|
||||
@OneToOne
|
||||
@JoinColumn(name = "user_id", nullable = false)
|
||||
@JoinColumn(name = "user_id")
|
||||
private User user;
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "diploma_topic_id", nullable = false)
|
||||
private DiplomaTopic diplomaTopic;
|
||||
@JoinColumn(name = "diploma_topic_id")
|
||||
private DiplomaTopic diplomaTopic;
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "mentor_user_id", nullable = false)
|
||||
@JoinColumn(name = "mentor_user_id")
|
||||
private User mentorUser;
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "group_id", nullable = false)
|
||||
@JoinColumn(name = "group_id")
|
||||
private Group group;
|
||||
|
||||
}
|
||||
|
||||
@ -2,10 +2,11 @@ package ru.tubryansk.tdms.entity;
|
||||
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
import org.hibernate.annotations.UpdateTimestamp;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
@ -15,40 +16,45 @@ import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@Entity
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Table(name = "user", schema = "vkr")
|
||||
@ToString
|
||||
@Entity
|
||||
@Table(name = "`user`")
|
||||
public class User implements UserDetails {
|
||||
@Id
|
||||
@Column(name = "id")
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Integer id;
|
||||
@Column(name = "login", nullable = false, unique = true)
|
||||
private Long id;
|
||||
@Column(name = "login")
|
||||
private String login;
|
||||
@Column(name = "password", nullable = false)
|
||||
@Column(name = "password")
|
||||
private String password;
|
||||
@Column(name = "full_name", nullable = false)
|
||||
@Column(name = "full_name")
|
||||
private String fullName;
|
||||
@Column(name = "mail", nullable = false, unique = true)
|
||||
private String mail;
|
||||
@Column(name = "number_phone", nullable = false, unique = true)
|
||||
@Column(name = "email")
|
||||
private String email;
|
||||
@Column(name = "number_phone")
|
||||
private String numberPhone;
|
||||
@Column(name = "created_at", nullable = false)
|
||||
@Column(name = "created_at")
|
||||
@CreationTimestamp
|
||||
private ZonedDateTime createdAt;
|
||||
@Column(name = "updated_at")
|
||||
@UpdateTimestamp
|
||||
private ZonedDateTime updatedAt;
|
||||
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
||||
@JoinTable(name = "user_role",schema = "vkr",
|
||||
joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
|
||||
inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"))
|
||||
@ManyToMany
|
||||
@JoinTable(
|
||||
name = "user_role",
|
||||
joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
|
||||
inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"))
|
||||
private List<Role> roles;
|
||||
|
||||
@Override
|
||||
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||
return roles.stream().map(Role::getAuthority).map(SimpleGrantedAuthority::new).toList();
|
||||
return roles.stream()
|
||||
.map(Role::getAuthority)
|
||||
.map(SimpleGrantedAuthority::new)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -0,0 +1,4 @@
|
||||
package ru.tubryansk.tdms.entity.repository;
|
||||
|
||||
public interface DefenceRepository {
|
||||
}
|
||||
@ -1,9 +1,13 @@
|
||||
package ru.tubryansk.tdms.repository;
|
||||
package ru.tubryansk.tdms.entity.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import ru.tubryansk.tdms.entity.DiplomaTopic;
|
||||
import ru.tubryansk.tdms.exception.NotFoundException;
|
||||
|
||||
@Repository
|
||||
public interface DiplomaTopicRepository extends JpaRepository<DiplomaTopic, Integer> {
|
||||
default DiplomaTopic findByIdThrow(Integer id) {
|
||||
return this.findById(id).orElseThrow(() -> new NotFoundException(DiplomaTopic.class, id));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package ru.tubryansk.tdms.entity.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import ru.tubryansk.tdms.entity.Group;
|
||||
import ru.tubryansk.tdms.exception.NotFoundException;
|
||||
|
||||
@Repository
|
||||
public interface GroupRepository extends JpaRepository<Group, Long> {
|
||||
default Group findByIdThrow(Long id) {
|
||||
return this.findById(id).orElseThrow(() -> new NotFoundException(Group.class, id));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
package ru.tubryansk.tdms.entity.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import ru.tubryansk.tdms.entity.Role;
|
||||
|
||||
@Repository
|
||||
public interface RoleRepository extends JpaRepository<Role, Long> {
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
package ru.tubryansk.tdms.entity.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import ru.tubryansk.tdms.entity.Student;
|
||||
import ru.tubryansk.tdms.entity.User;
|
||||
import ru.tubryansk.tdms.exception.NotFoundException;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Repository
|
||||
public interface StudentRepository extends JpaRepository<Student, Integer> {
|
||||
default Student findByIdThrow(Integer id) {
|
||||
return this.findById(id).orElseThrow(() -> new NotFoundException(Student.class, id));
|
||||
}
|
||||
|
||||
Optional<Student> findByUser(User user);
|
||||
}
|
||||
@ -1,7 +1,6 @@
|
||||
package ru.tubryansk.tdms.repository;
|
||||
package ru.tubryansk.tdms.entity.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;
|
||||
@ -1,6 +1,6 @@
|
||||
package ru.tubryansk.tdms.exception;
|
||||
|
||||
import ru.tubryansk.tdms.dto.ErrorResponse;
|
||||
import ru.tubryansk.tdms.controller.payload.ErrorResponse;
|
||||
|
||||
public class AccessDeniedException extends BusinessException {
|
||||
public AccessDeniedException() {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
package ru.tubryansk.tdms.exception;
|
||||
|
||||
import ru.tubryansk.tdms.dto.ErrorResponse;
|
||||
import ru.tubryansk.tdms.controller.payload.ErrorResponse;
|
||||
|
||||
public class BusinessException extends RuntimeException {
|
||||
public BusinessException(String message) {
|
||||
@ -8,6 +8,6 @@ public class BusinessException extends RuntimeException {
|
||||
}
|
||||
|
||||
public ErrorResponse.ErrorCode getErrorCode() {
|
||||
return ErrorResponse.ErrorCode.INTERNAL_ERROR;
|
||||
return ErrorResponse.ErrorCode.BUSINESS_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,42 +2,55 @@ package ru.tubryansk.tdms.exception;
|
||||
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.support.DefaultMessageSourceResolvable;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import org.springframework.web.servlet.resource.NoResourceFoundException;
|
||||
import ru.tubryansk.tdms.dto.ErrorResponse;
|
||||
import ru.tubryansk.tdms.controller.payload.ErrorResponse;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@RestControllerAdvice
|
||||
@Slf4j
|
||||
public class GlobalExceptionHandler {
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
@ExceptionHandler(BindException.class)
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public ErrorResponse handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
|
||||
// todo: make a better error message
|
||||
return new ErrorResponse(e.getMessage(), ErrorResponse.ErrorCode.VALIDATION_ERROR);
|
||||
public ErrorResponse handleMethodArgumentNotValidException(BindException e) {
|
||||
log.debug("Validation error: {}", e.getMessage());
|
||||
String validationErrors = e.getAllErrors().stream()
|
||||
.map(DefaultMessageSourceResolvable::getDefaultMessage)
|
||||
.collect(Collectors.joining(", "));
|
||||
return new ErrorResponse(validationErrors, ErrorResponse.ErrorCode.VALIDATION_ERROR);
|
||||
}
|
||||
|
||||
@ExceptionHandler(BusinessException.class)
|
||||
public ErrorResponse handleBusinessException(BusinessException e, HttpServletResponse response) {
|
||||
log.info("Business error", e);
|
||||
response.setStatus(e.getErrorCode().getHttpStatus().value());
|
||||
return new ErrorResponse(e.getMessage(), e.getErrorCode());
|
||||
}
|
||||
|
||||
@ExceptionHandler(org.springframework.security.access.AccessDeniedException.class)
|
||||
@ResponseStatus(HttpStatus.FORBIDDEN)
|
||||
public ErrorResponse handleAccessDeniedException(AccessDeniedException e) {
|
||||
log.debug("Access denied", e);
|
||||
return new ErrorResponse("", ErrorResponse.ErrorCode.ACCESS_DENIED);
|
||||
}
|
||||
|
||||
|
||||
@ExceptionHandler(NoResourceFoundException.class)
|
||||
@ResponseStatus(HttpStatus.NOT_FOUND)
|
||||
public ErrorResponse handleNoResourceFoundException(NoResourceFoundException e) {
|
||||
// todo: make error page
|
||||
log.error("Resource not found", e);
|
||||
return new ErrorResponse(e.getMessage(), ErrorResponse.ErrorCode.NOT_FOUND);
|
||||
}
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
public ErrorResponse handleUnexpectedException(Exception e) {
|
||||
// todo: make error page
|
||||
log.error("Unexpected exception.", e);
|
||||
return new ErrorResponse(e.getMessage(), ErrorResponse.ErrorCode.INTERNAL_ERROR);
|
||||
}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
package ru.tubryansk.tdms.exception;
|
||||
|
||||
import ru.tubryansk.tdms.dto.ErrorResponse;
|
||||
import ru.tubryansk.tdms.controller.payload.ErrorResponse;
|
||||
|
||||
public class NotFoundException extends BusinessException {
|
||||
public NotFoundException(Class<?> entityClass, Integer id) {
|
||||
super(entityClass.getSimpleName() + " with id " + id + " not found");
|
||||
public NotFoundException(Class<?> entityClass, Object id) {
|
||||
super(entityClass.getSimpleName() + " с идентификатором " + id + " не наеден");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -0,0 +1,51 @@
|
||||
package ru.tubryansk.tdms.service;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import ru.tubryansk.tdms.entity.User;
|
||||
|
||||
import static org.springframework.security.web.context.HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class AuthenticationService {
|
||||
@Autowired
|
||||
private HttpServletRequest request;
|
||||
@Autowired
|
||||
private AuthenticationManager authenticationManager;
|
||||
|
||||
public boolean authenticated() {
|
||||
var authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
return authentication.isAuthenticated() && (authentication.getPrincipal() instanceof User);
|
||||
}
|
||||
|
||||
public void logout() {
|
||||
HttpSession session = request.getSession(false);
|
||||
if(session != null) {
|
||||
session.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void login(String username, String password) {
|
||||
try {
|
||||
var context = SecurityContextHolder.createEmptyContext();
|
||||
var token = new UsernamePasswordAuthenticationToken(username, password);
|
||||
var authenticated = authenticationManager.authenticate(token);
|
||||
context.setAuthentication(authenticated);
|
||||
SecurityContextHolder.setContext(context);
|
||||
request.getSession(true).setAttribute(SPRING_SECURITY_CONTEXT_KEY, context);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to log in user: {}", username, e);
|
||||
throw e;
|
||||
}
|
||||
log.debug("User {} logged in", username);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
package ru.tubryansk.tdms.service;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.tubryansk.tdms.controller.payload.UserDTO;
|
||||
import ru.tubryansk.tdms.entity.User;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class CallerService {
|
||||
@Autowired
|
||||
private AuthenticationService authenticationService;
|
||||
|
||||
public Optional<User> getCallerUser() {
|
||||
if(authenticationService.authenticated()) {
|
||||
return Optional.of((User) SecurityContextHolder.getContext().getAuthentication().getPrincipal());
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public UserDTO getCallerUserDTO() {
|
||||
return getCallerUser().map(UserDTO::from).orElse(UserDTO.unauthenticated());
|
||||
}
|
||||
}
|
||||
@ -1,31 +1,9 @@
|
||||
package ru.tubryansk.tdms.service;
|
||||
|
||||
import jakarta.transaction.Transactional;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.tubryansk.tdms.entity.DiplomaTopic;
|
||||
import ru.tubryansk.tdms.exception.NotFoundException;
|
||||
import ru.tubryansk.tdms.repository.DiplomaTopicRepository;
|
||||
import ru.tubryansk.tdms.dto.DiplomaTopicDTO;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@Transactional
|
||||
public class DiplomaTopicService {
|
||||
@Autowired
|
||||
private DiplomaTopicRepository diplomaTopicRepository;
|
||||
|
||||
public List<DiplomaTopicDTO> getAll() {
|
||||
return diplomaTopicRepository.findAll()
|
||||
.stream()
|
||||
.map(DiplomaTopicDTO::fromEntity)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public DiplomaTopicDTO getById(Integer id) {
|
||||
return DiplomaTopicDTO.fromEntity(diplomaTopicRepository.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException(DiplomaTopic.class, id)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,15 @@
|
||||
package ru.tubryansk.tdms.service;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import ru.tubryansk.tdms.controller.payload.GroupDTO;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@Service
|
||||
@Transactional
|
||||
public class GroupService {
|
||||
public Collection<GroupDTO> getAllGroups() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
package ru.tubryansk.tdms.service;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.event.ContextStartedEvent;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class LifeCycleService {
|
||||
@EventListener(ContextStartedEvent.class)
|
||||
public void onStartup(ContextStartedEvent event) {
|
||||
ApplicationContext applicationContext = event.getApplicationContext();
|
||||
log.info("Static files location: {}", applicationContext.getEnvironment().getProperty("spring.web.resources.static-locations"));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
package ru.tubryansk.tdms.service;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import ru.tubryansk.tdms.entity.Role;
|
||||
import ru.tubryansk.tdms.entity.repository.RoleRepository;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@Service
|
||||
public class RoleService {
|
||||
public enum Authority {
|
||||
ROLE_ADMINISTRATOR,
|
||||
ROLE_COMMISSION_MEMBER,
|
||||
ROLE_TEACHER,
|
||||
ROLE_SECRETARY,
|
||||
ROLE_STUDENT,
|
||||
}
|
||||
|
||||
public transient Map<String, Role> roles;
|
||||
|
||||
@Autowired
|
||||
private RoleRepository roleRepository;
|
||||
|
||||
@PostConstruct
|
||||
@Transactional
|
||||
public void init() {
|
||||
roles = new ConcurrentHashMap<>();
|
||||
roleRepository.findAll().forEach(role -> roles.put(role.getAuthority(), role));
|
||||
}
|
||||
|
||||
public Role getRoleByAuthority(Authority authority) {
|
||||
return roles.get(authority.name());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
package ru.tubryansk.tdms.service;
|
||||
|
||||
import jakarta.transaction.Transactional;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.tubryansk.tdms.controller.payload.StudentDTO;
|
||||
import ru.tubryansk.tdms.entity.DiplomaTopic;
|
||||
import ru.tubryansk.tdms.entity.Student;
|
||||
import ru.tubryansk.tdms.entity.repository.DiplomaTopicRepository;
|
||||
import ru.tubryansk.tdms.entity.repository.StudentRepository;
|
||||
import ru.tubryansk.tdms.exception.AccessDeniedException;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
@Transactional
|
||||
public class StudentService {
|
||||
@Autowired
|
||||
private StudentRepository studentRepository;
|
||||
@Autowired
|
||||
private DiplomaTopicRepository diplomaTopicRepository;
|
||||
@Autowired
|
||||
private Optional<Student> student;
|
||||
@Autowired
|
||||
private CallerService callerService;
|
||||
|
||||
/** @param studentToDiplomaTopic Map of @{@link Student} id and @{@link DiplomaTopic} id */
|
||||
public void changeDiplomaTopic(Map<Integer, Integer> studentToDiplomaTopic) {
|
||||
studentToDiplomaTopic.forEach(this::changeDiplomaTopic);
|
||||
}
|
||||
|
||||
public void changeDiplomaTopic(Integer studentId, Integer diplomaTopicId) {
|
||||
Student student = studentRepository.findByIdThrow(studentId);
|
||||
DiplomaTopic diplomaTopic = diplomaTopicRepository.findByIdThrow(diplomaTopicId);
|
||||
student.setDiplomaTopic(diplomaTopic);
|
||||
}
|
||||
|
||||
public void changeCallerDiplomaTopic(Integer diplomaTopicId) {
|
||||
DiplomaTopic diplomaTopic = diplomaTopicRepository.findByIdThrow(diplomaTopicId);
|
||||
student.ifPresentOrElse(s -> s.setDiplomaTopic(diplomaTopic), () -> {throw new AccessDeniedException();});
|
||||
}
|
||||
|
||||
public Optional<Student> getCallerStudent() {
|
||||
return studentRepository.findByUser(callerService.getCallerUser().orElse(null));
|
||||
}
|
||||
|
||||
public StudentDTO getCallerStudentDTO() {
|
||||
Student callerStudent = getCallerStudent().orElse(null);
|
||||
if (callerStudent == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return StudentDTO.from(callerStudent);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
package ru.tubryansk.tdms.service;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class SysInfoService {
|
||||
@Value("${application.version}")
|
||||
private String version;
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
}
|
||||
@ -1,19 +1,23 @@
|
||||
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.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.tubryansk.tdms.controller.payload.RegistrationDTO;
|
||||
import ru.tubryansk.tdms.controller.payload.UserDTO;
|
||||
import ru.tubryansk.tdms.entity.Role;
|
||||
import ru.tubryansk.tdms.entity.Student;
|
||||
import ru.tubryansk.tdms.entity.User;
|
||||
import ru.tubryansk.tdms.repository.UserRepository;
|
||||
import ru.tubryansk.tdms.entity.repository.GroupRepository;
|
||||
import ru.tubryansk.tdms.entity.repository.StudentRepository;
|
||||
import ru.tubryansk.tdms.entity.repository.UserRepository;
|
||||
|
||||
import static org.springframework.security.web.context.HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
@Transactional
|
||||
@ -22,32 +26,74 @@ 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);
|
||||
}
|
||||
private GroupRepository groupRepository;
|
||||
@Autowired
|
||||
private StudentRepository studentRepository;
|
||||
@Autowired
|
||||
private RoleService roleService;
|
||||
@Autowired
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
@Override
|
||||
public User loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
return userRepository.findUserByLogin(username)
|
||||
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
|
||||
log.info("Loading user with username: {}", username);
|
||||
User user = userRepository.findUserByLogin(username).orElseThrow(() -> {
|
||||
log.info("User with login {} not found", username);
|
||||
return new UsernameNotFoundException("User with login " + username + " not found");
|
||||
});
|
||||
log.info("User with login {} loaded", username);
|
||||
return user;
|
||||
}
|
||||
|
||||
public void logout() {
|
||||
HttpSession session = httpServletRequest.getSession(true);
|
||||
// if(session != null) {
|
||||
// session.invalidate();
|
||||
// }
|
||||
session.setAttribute(SPRING_SECURITY_CONTEXT_KEY, null);
|
||||
public List<UserDTO> getAllUsers() {
|
||||
log.info("Loading all users");
|
||||
List<UserDTO> users = userRepository.findAll().stream()
|
||||
.map(UserDTO::from)
|
||||
.toList();
|
||||
log.info("{} users loaded", users.size());
|
||||
return users;
|
||||
}
|
||||
|
||||
public void registerUser(RegistrationDTO registrationDTO) {
|
||||
log.info("Registering new user with login: {}", registrationDTO.getLogin());
|
||||
User user = transientUser(registrationDTO);
|
||||
Student student = transientStudent(registrationDTO.getStudentData());
|
||||
fillRoles(user, registrationDTO);
|
||||
|
||||
log.info("Saving new user: {}", user);
|
||||
userRepository.save(user);
|
||||
if (student != null) {
|
||||
student.setUser(user);
|
||||
log.info("User is student, saving student: {}", student);
|
||||
studentRepository.save(student);
|
||||
}
|
||||
}
|
||||
|
||||
private User transientUser(RegistrationDTO registrationDTO) {
|
||||
User user = new User();
|
||||
user.setLogin(registrationDTO.getLogin());
|
||||
user.setPassword(passwordEncoder.encode(registrationDTO.getPassword()));
|
||||
user.setFullName(registrationDTO.getFullName());
|
||||
user.setEmail(registrationDTO.getEmail());
|
||||
user.setNumberPhone(registrationDTO.getNumberPhone());
|
||||
return user;
|
||||
}
|
||||
|
||||
private Student transientStudent(RegistrationDTO.StudentRegistrationDTO studentData) {
|
||||
if (studentData == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Student student = new Student();
|
||||
student.setGroup(groupRepository.findByIdThrow(studentData.getGroupId()));
|
||||
return student;
|
||||
}
|
||||
|
||||
private void fillRoles(User user, RegistrationDTO registrationDTO) {
|
||||
List<Role> roles = new ArrayList<>();
|
||||
if (registrationDTO.getStudentData() != null) {
|
||||
roles.add(roleService.getRoleByAuthority(RoleService.Authority.ROLE_STUDENT));
|
||||
}
|
||||
user.setRoles(roles);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
package ru.tubryansk.tdms.web;
|
||||
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class LoggingRequestFilter extends OncePerRequestFilter {
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
||||
long startTime = System.currentTimeMillis();
|
||||
log.info("Making request: {}. user: {}, session: {}, remote ip: {}",
|
||||
request.getRequestURI(), request.getRemoteUser(),
|
||||
request.getSession().getId(), request.getRemoteAddr());
|
||||
try {
|
||||
filterChain.doFilter(request, response);
|
||||
} finally {
|
||||
long duration = System.currentTimeMillis() - startTime;
|
||||
log.info("Request finished with {} status. duration: {} ms", response.getStatus(), duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
package ru.tubryansk.tdms.web;
|
||||
|
||||
import jakarta.servlet.http.HttpSessionEvent;
|
||||
import jakarta.servlet.http.HttpSessionListener;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class LoggingSessionListener implements HttpSessionListener {
|
||||
@Override
|
||||
public void sessionCreated(HttpSessionEvent se) {
|
||||
log.debug("Session created: {}, user {}",
|
||||
se.getSession().getId(), SecurityContextHolder.getContext().getAuthentication().getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sessionDestroyed(HttpSessionEvent se) {
|
||||
log.debug("Session destroyed: {}, user: {}",
|
||||
se.getSession().getId(), SecurityContextHolder.getContext().getAuthentication().getName());
|
||||
}
|
||||
}
|
||||
@ -4,8 +4,6 @@ application:
|
||||
domain: localhost
|
||||
protocol: http
|
||||
spring:
|
||||
flyway:
|
||||
out-of-order: true
|
||||
web:
|
||||
resources:
|
||||
static-locations: file:///${user.dir}/web/dist/
|
||||
|
||||
@ -1,19 +1,19 @@
|
||||
db:
|
||||
url: jdbc:postgresql://localhost:5432/db
|
||||
url: jdbc:postgresql://localhost:5400/tdms
|
||||
schema: public
|
||||
user: root
|
||||
password: root
|
||||
schema: vkr
|
||||
application:
|
||||
name: @name@
|
||||
version: @version@
|
||||
type: production
|
||||
port: 80
|
||||
domain: tdms.tu-bryansk.ru
|
||||
protocol: https
|
||||
protocol: http
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: tdms
|
||||
name: ${application.name}
|
||||
datasource:
|
||||
url: ${db.url}
|
||||
username: ${db.user}
|
||||
@ -28,7 +28,8 @@ spring:
|
||||
user: ${db.user}
|
||||
password: ${db.password}
|
||||
schemas: ${db.schema}
|
||||
locations: db.migration
|
||||
main:
|
||||
banner-mode: off
|
||||
server:
|
||||
port: ${application.port}
|
||||
address: ${application.domain}
|
||||
|
||||
@ -1,6 +0,0 @@
|
||||
create table vkr.role
|
||||
(
|
||||
id integer primary key generated by default as identity,
|
||||
name text not null unique,
|
||||
authority text not null unique
|
||||
)
|
||||
@ -1,12 +0,0 @@
|
||||
create table vkr.user
|
||||
(
|
||||
id integer primary key generated by default as identity,
|
||||
login text not null unique,
|
||||
password text not null,
|
||||
full_name text not null,
|
||||
mail text not null unique,
|
||||
number_phone text not null unique,
|
||||
created_at timestamp(6) with time zone not null,
|
||||
updated_at timestamp(6) with time zone
|
||||
|
||||
)
|
||||
@ -1,9 +0,0 @@
|
||||
create table vkr.user_role
|
||||
(
|
||||
user_id integer not null,
|
||||
role_id integer not null,
|
||||
foreign key (user_id) references vkr.user (id)
|
||||
on delete cascade,
|
||||
foreign key (role_id) references vkr.role (id)
|
||||
on delete cascade
|
||||
)
|
||||
@ -1,5 +0,0 @@
|
||||
create table vkr.diploma_topic
|
||||
(
|
||||
id integer primary key generated by default as identity,
|
||||
name text not null unique
|
||||
)
|
||||
@ -1,9 +0,0 @@
|
||||
create table vkr.group
|
||||
(
|
||||
id integer primary key generated by default as identity,
|
||||
name text not null unique,
|
||||
principal_user_id integer not null,
|
||||
foreign key (principal_user_id) references vkr.user (id)
|
||||
on delete cascade
|
||||
|
||||
)
|
||||
@ -1,24 +0,0 @@
|
||||
create table vkr.student
|
||||
(
|
||||
id integer primary key generated by default as identity,
|
||||
form boolean,
|
||||
protection_order integer not null,
|
||||
magistracy text,
|
||||
digital_format_present boolean,
|
||||
mark_comment integer,
|
||||
mark_practice integer,
|
||||
predefence_comment text,
|
||||
normal_control text,
|
||||
anti_plagiarism int,
|
||||
note text,
|
||||
record_book_returned boolean,
|
||||
work text,
|
||||
user_id integer not null,
|
||||
diploma_topic_id integer not null,
|
||||
mentor_user_id integer not null,
|
||||
group_id integer not null,
|
||||
foreign key (user_id) references vkr.user (id) on delete cascade,
|
||||
foreign key (diploma_topic_id) references vkr.diploma_topic (id) on delete cascade,
|
||||
foreign key (mentor_user_id) references vkr.user (id) on delete cascade,
|
||||
foreign key (group_id) references vkr.group (id) on delete cascade
|
||||
)
|
||||
@ -1,96 +0,0 @@
|
||||
INSERT INTO vkr.user (login, password, full_name, mail, number_phone, created_at, updated_at)
|
||||
VALUES ('akulenko_mikhail', 'password123', 'Акуленко Михаил Вячеславович', 'akulenko.mikhail@example.com',
|
||||
'+79110000001', NOW(), NOW()),
|
||||
('borovikov_artem', 'password123', 'Боровиков Артём Викторович', 'borovikov.artem@example.com', '+79110000002',
|
||||
NOW(), NOW()),
|
||||
('bykonya_alexey', 'password123', 'Быконя Алексей Николаевич', 'bykonya.alexey@example.com', '+79110000003',
|
||||
NOW(), NOW()),
|
||||
('ermakov_alexander', 'password123', 'Ермаков Александр Сергеевич', 'ermakov.alexander@example.com',
|
||||
'+79110000004', NOW(), NOW()),
|
||||
('zgursky_evgeny', 'password123', 'Згурский Евгений Олегович', 'zgursky.evgeny@example.com', '+79110000005',
|
||||
NOW(), NOW()),
|
||||
('ibishov_tural', 'password123', 'Ибишов Турал Садай оглы', 'ibishov.tural@example.com', '+79110000006', NOW(),
|
||||
NOW()),
|
||||
('ignatenko_vladimir', 'password123', 'Игнатенко Владимир Алексеевич', 'ignatenko.vladimir@example.com',
|
||||
'+79110000007', NOW(), NOW()),
|
||||
('lazukin_danila', 'password123', 'Лазукин Данила Дмитриевич', 'lazukin.danila@example.com', '+79110000008',
|
||||
NOW(), NOW()),
|
||||
('mitiaev_danila', 'password123', 'Митяев Данила Алексеевич', 'mitiaev.danila@example.com', '+79110000009',
|
||||
NOW(), NOW()),
|
||||
('neshkov_daniil', 'password123', 'Нешков Даниил Владимирович', 'neshkov.daniil@example.com', '+79110000010',
|
||||
NOW(), NOW()),
|
||||
('petrov_pavel', 'password123', 'Петров Павел Сергеевич', 'petrov.pavel@example.com', '+79110000011', NOW(),
|
||||
NOW()),
|
||||
('sazonov_andrey', 'password123', 'Сазонов Андрей Андреевич', 'sazonov.andrey@example.com', '+79110000012',
|
||||
NOW(), NOW()),
|
||||
('solokhin_maxim', 'password123', 'Солохин Максим Николаевич', 'solokhin.maxim@example.com', '+79110000013',
|
||||
NOW(), NOW()),
|
||||
('sochinsky_artem', 'password123', 'Сочинский Артем Александрович', 'sochinsky.artem@example.com',
|
||||
'+79110000014', NOW(), NOW()),
|
||||
('trisvyatsky_kirill', 'password123', 'Трисвятский Кирилл Андреевич', 'trisvyatsky.kirill@example.com',
|
||||
'+79110000015', NOW(), NOW()),
|
||||
('turov_alexander', 'password123', 'Туров Александр Сергеевич', 'turov.alexander@example.com', '+79110000016',
|
||||
NOW(), NOW()),
|
||||
('shevtsova_alexandra', 'password123', 'Шевцова Александра Валерьевна', 'shevtsova.alexandra@example.com',
|
||||
'+79110000017', NOW(), NOW()),
|
||||
('kibalyuk_artem', 'password123', 'Кибалюк Артем Сергеевич', 'kibalyuk.artem@example.com', '+79110000018', NOW(),
|
||||
NOW()),
|
||||
('shulindin_artem', 'password123', 'Шулындин Артём Андреевич', 'shulindin.artem@example.com', '+79110000019',
|
||||
NOW(), NOW()),
|
||||
('belyaev_egor', 'password123', 'Беляев Егор Андреевич', 'belyaev.egor@example.com', '+79110000020', NOW(),
|
||||
NOW()),
|
||||
('berezhnoy_igor', 'password123', 'Бережной Игорь Александрович', 'berezhnoy.igor@example.com', '+79110000021',
|
||||
NOW(), NOW()),
|
||||
('bogun_pavel', 'password123', 'Богун Павел Сергеевич', 'bogun.pavel@example.com', '+79110000022', NOW(), NOW()),
|
||||
('vaseykin_nikita', 'password123', 'Васейкин Никита Павлович', 'vaseykin.nikita@example.com', '+79110000023',
|
||||
NOW(), NOW()),
|
||||
('gomonov_nikita', 'password123', 'Гомонов Никита Алексеевич', 'gomonov.nikita@example.com', '+79110000024',
|
||||
NOW(), NOW()),
|
||||
('druyan_oleg', 'password123', 'Друян Олег Викторович', 'druyan.oleg@example.com', '+79110000025', NOW(), NOW()),
|
||||
('ivanov_kirill', 'password123', 'Иванов Кирилл Эдуардович', 'ivanov.kirill@example.com', '+79110000026', NOW(),
|
||||
NOW()),
|
||||
('ivanova_veronika', 'password123', 'Иванова Вероника Евгеньевна', 'ivanova.veronika@example.com',
|
||||
'+79110000027', NOW(), NOW()),
|
||||
('izotov_ivan', 'password123', 'Изотов Иван Алексеевич', 'izotov.ivan@example.com', '+79110000028', NOW(),
|
||||
NOW()),
|
||||
('isakov_zahar', 'password123', 'Исаков Захар Александрович', 'isakov.zahar@example.com', '+79110000029', NOW(),
|
||||
NOW()),
|
||||
('iskritsky_daniil', 'password123', 'Искрицкий Даниил Павлович', 'iskritsky.daniil@example.com', '+79110000030',
|
||||
NOW(), NOW()),
|
||||
('linko_daria', 'password123', 'Линько Дарья Андреевна', 'linko.daria@example.com', '+79110000031', NOW(),
|
||||
NOW()),
|
||||
('logutov_kirill', 'password123', 'Логутов Кирилл Александрович', 'logutov.kirill@example.com', '+79110000032',
|
||||
NOW(), NOW()),
|
||||
('nekrassov_sergey', 'password123', 'Некрасов Сергей Игоревич', 'nekrassov.sergey@example.com', '+79110000033',
|
||||
NOW(), NOW()),
|
||||
('sinyagin_ilya', 'password123', 'Синягин Илья Александрович', 'sinyagin.ilya@example.com', '+79110000034',
|
||||
NOW(), NOW()),
|
||||
('sopriko_daniil', 'password123', 'Соприко Даниил Сергеевич', 'sopriko.daniil@example.com', '+79110000035',
|
||||
NOW(), NOW()),
|
||||
('turovsky_ivan', 'password123', 'Туровский Иван Алексеевич', 'turovsky.ivan@example.com', '+79110000036', NOW(),
|
||||
NOW()),
|
||||
('frantsev_sergey', 'password123', 'Францев Сергей Дмитриевич', 'frantsev.sergey@example.com', '+79110000037',
|
||||
NOW(), NOW()),
|
||||
('chepurnoy_maxim', 'password123', 'Чепурной Максим Романович', 'chepurnoy.maxim@example.com', '+79110000038',
|
||||
NOW(), NOW()),
|
||||
('schemelinin_dmitry', 'password123', 'Щемелинин Дмитрий Михайлович', 'schemelinin.dmitry@example.com',
|
||||
'+79110000039', NOW(), NOW()),
|
||||
('bulatitsky_d_i_1', 'password123', 'Булатицкий Д. И.', 'bulatitsky.d.i.1@example.com', '+79110000040', NOW(),
|
||||
NOW()),
|
||||
('kopeliovich_d_i_1', 'password123', 'Копелиович Д. И.', 'kopeliovich.d.i.1@example.com', '+79110000041', NOW(),
|
||||
NOW()),
|
||||
('dergachev_k_v', 'password123', 'Дергачев К. В.', 'dergachev.k.v@example.com', '+79110000042', NOW(), NOW()),
|
||||
('trubakov_e_o', 'password123', 'Трубаков Е. О.', 'trubakov.e.o@example.com', '+79110000043', NOW(), NOW()),
|
||||
('radchenko_a_o', 'password123', 'Радченко А. О.', 'radchenko.a.o@example.com', '+79110000044', NOW(), NOW()),
|
||||
('zimin_s_n_1', 'password123', 'Зимин С. Н.', 'zimin.s.n.1@example.com', '+79110000045', NOW(), NOW()),
|
||||
('koptenok_e_v', 'password123', 'Коптенок Е. В.', 'koptenok.e.v@example.com', '+79110000046', NOW(), NOW()),
|
||||
('mikhaleva_o_a', 'password123', 'Михалева О. А.', 'mikhaleva.o.a@example.com', '+79110000047', NOW(), NOW()),
|
||||
('gulakov_k_v', 'password123', 'Гулаков К. В.', 'gulakov.k.v@example.com', '+79110000048', NOW(), NOW()),
|
||||
('titarev_d_v', 'password123', 'Титарёв Д. В.', 'titarev.d.v@example.com', '+79110000049', NOW(), NOW()),
|
||||
('izrailev_v_ya_1', 'password123', 'Израилев В. Я.', 'izrailev.v.ya.1@example.com', '+79110000050', NOW(),
|
||||
NOW()),
|
||||
('podvesovsky_a_g_1', 'password123', 'Подвесовский А. Г.', 'podvesovsky.a.g.1@example.com', '+79110000051',
|
||||
NOW(), NOW()),
|
||||
('trubakov_a_o', 'password123', 'Трубаков А. О.', 'trubakov.a.o@example.com', '+79110000059',
|
||||
NOW(), NOW()),
|
||||
('lageriev_d_g', 'password123', 'Лагерев Д. Г.', 'lageriev.d.g@example.com', '+79110000052', NOW(), NOW());
|
||||
@ -1 +0,0 @@
|
||||
UPDATE vkr.user SET password = '{noop}1';
|
||||
@ -1,2 +0,0 @@
|
||||
INSERT INTO vkr.group (name ,principal_user_id)
|
||||
VALUES('ИВТ-1',40),('ИВТ-2',40);
|
||||
@ -1,2 +0,0 @@
|
||||
INSERT INTO vkr.role (name,authority)
|
||||
VALUES('Руководитель','ROLE_DIRECTOR'),('Куратор','ROLE_TUTOR'),('Студент','ROLE_STUDENT');
|
||||
@ -1,73 +0,0 @@
|
||||
INSERT INTO vkr.student (form,
|
||||
protection_order,
|
||||
magistracy,
|
||||
digital_format_present,
|
||||
mark_comment,
|
||||
mark_practice,
|
||||
predefence_comment,
|
||||
normal_control,
|
||||
anti_plagiarism,
|
||||
note,
|
||||
record_book_returned,
|
||||
work,
|
||||
user_id,
|
||||
diploma_topic_id,
|
||||
mentor_user_id,
|
||||
group_id)
|
||||
VALUES (true, 1100, 'ПРИ, ИВТ или другой вуз', true, 5, 5, 'ок', 'Подписано', 7862, 'Акт о внедрении', true, 'нет', 1,
|
||||
1, 40, 1),
|
||||
(true, 1110, 'Да, но не уверен, в БГТУ ли', true, 5, 5, 'ок', 'Подписано', 8196, 'Заявка, Акт о внедрении', true,
|
||||
'ООО "ЦИРОБЗ"', 2, 2, 40, 1),
|
||||
(true, 3500, 'Нет', true, 3, 3, 'Критически низкий уровень. Допущен под ответственность руководителя ВКР',
|
||||
'Подписано', 7141, 'Иниц', true, 'нет', 3, 3, 41, 1),
|
||||
(true, 1800, 'Да, но не уверен, в БГТУ ли', true, 4, 5, 'Усилить работу', 'Подписано', 5381, 'Иниц', true, 'нет',
|
||||
4, 4, 42, 1),
|
||||
(true, 2100, 'ИВТ, ПРИ', true, 5, 5, 'ок', 'Подписано', 8146, 'Заявка, Акт о внедрении', true, 'нет', 5, 5, 43,
|
||||
1),
|
||||
(true, 1100, 'ИВТ, ПРИ', true, 5, 5, 'ок', 'Подписано', 7965, 'Иниц', true, 'нет', 6, 6, 41, 1),
|
||||
(true, 1200, 'нет', true, 5, 4, 'Усилить работу', 'Подписано', 8583, Null, true, 'Газ Энерго Комплект (ГЭК)', 7,
|
||||
7, 44, 1),
|
||||
(true, 1700, 'Нет', true, 5, 5, 'Критически низкий уровень. Допущен под ответственность руководителя ВКР',
|
||||
'Подписано', 6647, Null, true, 'нет', 8, 8, 45, 1),
|
||||
(true, 3300, 'ИВТ, ПРИ или другой вуз', true, 5, 5, 'Усилить работу', 'Подписано', 6741,
|
||||
'Заявка, Акт о внедрении', true, 'нет', 9, 9, 46, 1),
|
||||
(true, 1200, 'ИВТ', true, 5, 5, 'ок', 'Подписано', 7645, 'Заявка, Акт о внедрении', true, 'нет', 10, 10, 40, 1),
|
||||
(true, 3700, 'нет', true, 3, 3, 'Критически низкий уровень. Допущен под ответственность руководителя ВКР', '1',
|
||||
3093, 'Иниц', true, 'нет', 11, 11, 45, 1),
|
||||
(true, 1900, 'Нет', true, 5, 5, 'ок', 'Подписано', 8175, 'Заявка, Акт о внедрении', true, 'нет', 12, 12, 42, 1),
|
||||
(true, 3200, 'ИВТ', true, 5, 5, 'Усилить работу', 'Подписано', 7805, 'Акт', true, 'нет', 13, 13, 46, 1),
|
||||
(true, 1200, 'ИВТ, ПРИ', true, 4, 4, 'ок', 'рек', 7590, 'Иниц', true, 'нет', 14, 14, 45, 1),
|
||||
(true, 3900, 'нет', true, 5, 5, 'Усилить работу', 'Подписано', 6463, 'Заявка, Акт о внедрении', true, 'нет', 15,
|
||||
15, 40, 1),
|
||||
(true, 2200, 'ИВТ', true, 5, 5, 'ок', 'Подписано', 7441, Null, true, 'ООО "ЦИРОБЗ"', 16, 16, 44, 1),
|
||||
(true, 1130, 'Нет', true, 5, 4, 'Критически низкий уровень. Допущен под ответственность руководителя ВКР',
|
||||
'Подписано', 7319, 'Заявка, Акт о внедрении', true, 'нет', 17, 17, 40, 1),
|
||||
(true, 2400, 'ИВТ', true, 4, 5, 'ок', 'Подписано', 6436, Null, true, 'нет', 18, 18, 45, 1),
|
||||
(true, 1600, 'ИВТ', true, 5, 5, 'Усилить работу', 'Подписано', 6227, 'Исслед', true, 'АО "БЭМЗ"', 19, 19, 47, 1),
|
||||
(true, 1400, 'Нет', true, 5, 5, 'ок', 'Подписано', 8935, 'Иниц', true, 'ООО "ЦИРОБЗ"', 20, 20, 44, 2),
|
||||
(true, 2900, 'ИВТ', true, 5, 5, 'ок', 'Подписано', 7971, 'Заявка, Акт о внедрении', true, 'нет', 21, 21, 40, 2),
|
||||
(true, 2600, 'ИВТ, ПРИ', true, 3, 3, 'ок', 'Подписано', 7284, Null, true, 'нет', 22, 22, 48, 2),
|
||||
(true, 3100, 'ИВТ, ПРИ', true, 5, 5, 'Усилить работу', 'Подписано', 4966, 'Заявка, Акт о внедрении', true, 'нет',
|
||||
23, 23, 45, 2),
|
||||
(true, 3110, 'Нет', true, 5, 5, 'ок', 'Подписано', 7174, Null, true, 'нет', 24, 24, 40, 2),
|
||||
(true, 3800, 'Нет', true, 5, 5, 'ок', 'Подписано', 7233, Null, true, 'нет', 25, 25, 45, 2),
|
||||
(true, 3300, 'ИВТ', true, 5, 5, 'ок', 'Подписано', 7133, Null, true, 'нет', 26, 26, 43, 2),
|
||||
(true, 3130, 'ИВТ', true, 5, 5, 'ок', 'Подписано', 8083, 'Заявка, Акт о внедрении', true, 'нет', 27, 27, 49, 2),
|
||||
(true, 3400, 'Нет', true, 5, 4, 'Критически низкий уровень. Допущен под ответственность руководителя ВКР',
|
||||
'Подписано', 7968, 'Иниц', true, 'нет', 28, 28, 50, 2),
|
||||
(true, 3120, 'ИВТ или ПРИ', true, 5, 5, 'ок', 'Подписано', 7940, 'Исслед', true, 'ООО "ЦИРОБЗ"', 29, 29, 40, 2),
|
||||
(true, 2800, 'Нет (возможно)', true, 4, 4, 'Усилить работу', 'рек', 6775, 'Заявка, Акт о внедрении', true, 'нет',
|
||||
30, 30, 40, 2),
|
||||
(true, 2100, 'Нет (возможно)', true, 5, 5, 'ок', 'Подписано', 7637, 'Заявка, Акт о внедрении', true, 'нет', 31,
|
||||
31, 40, 2),
|
||||
(true, 2700, 'ИВТ', true, 5, 5, 'ок', 'Подписано', 8544, 'Заявка, Акт о внедрении', true, 'нет', 32, 32, 45, 2),
|
||||
(true, 2130, 'ИВТ, ПРИ', true, 5, 5, 'ок', 'Подписано', 7166, 'Заявка, Акт о внедрении', true, 'нет', 33, 33, 51,
|
||||
2),
|
||||
(true, 2110, 'ИВТ', true, 5, 5, 'ок', 'Подписано', 6075, 'Заявка, Акт о внедрении', true, 'нет', 34, 34, 52, 2),
|
||||
(true, 3100, 'ИВТ', true, 5, 5, 'ок', 'Подписано', 7057, 'Заявка, Акт о внедрении', true, 'нет', 35, 35, 50, 2),
|
||||
(true, 2120, 'В БГТУ на другой кафедре (38.04.01 Экономика или 27.04.05. Инноватика)', true, 5, 5, 'ок',
|
||||
'Подписано', 7057, 'Заявка, Акт о внедрении', true, 'нет', 36, 36, 51, 2),
|
||||
(true, 2500, 'ИВТ', true, 5, 5, 'ок', 'Подписано', 6583, 'Заявка, Акт о внедрении', true, 'нет', 37, 37, 53, 2),
|
||||
(true, 1300, 'ИВТ, ПРИ', true, 5, 5, 'ок', 'Подписано', 8444, 'Заявка, Акт о внедрении', true, 'нет', 38, 38, 44,
|
||||
2),
|
||||
(true, 3600, 'ИВТ', true, 5, 5, 'ок', 'Подписано', 7631, 'Заявка, Акт о внедрении', true, 'нет', 39, 39, 45, 2);
|
||||
@ -1,55 +0,0 @@
|
||||
INSERT INTO vkr.user_role (user_id, role_id)
|
||||
VALUES (1, 3),
|
||||
(2, 3),
|
||||
(3, 3),
|
||||
(4, 3),
|
||||
(5, 3),
|
||||
(6, 3),
|
||||
(7, 3),
|
||||
(8, 3),
|
||||
(9, 3),
|
||||
(10, 3),
|
||||
(11, 3),
|
||||
(12, 3),
|
||||
(13, 3),
|
||||
(14, 3),
|
||||
(15, 3),
|
||||
(16, 3),
|
||||
(17, 3),
|
||||
(18, 3),
|
||||
(19, 3),
|
||||
(20, 3),
|
||||
(21, 3),
|
||||
(22, 3),
|
||||
(23, 3),
|
||||
(24, 3),
|
||||
(25, 3),
|
||||
(26, 3),
|
||||
(27, 3),
|
||||
(28, 3),
|
||||
(29, 3),
|
||||
(30, 3),
|
||||
(31, 3),
|
||||
(32, 3),
|
||||
(33, 3),
|
||||
(34, 3),
|
||||
(35, 3),
|
||||
(36, 3),
|
||||
(37, 3),
|
||||
(38, 3),
|
||||
(39, 3),
|
||||
(40, 1),
|
||||
(41, 1),
|
||||
(42, 1),
|
||||
(43, 1),
|
||||
(44, 1),
|
||||
(45, 1),
|
||||
(46, 1),
|
||||
(47, 1),
|
||||
(48, 1),
|
||||
(49, 1),
|
||||
(50, 1),
|
||||
(51, 1),
|
||||
(52, 1),
|
||||
(53, 1),
|
||||
(40, 2);
|
||||
@ -0,0 +1,13 @@
|
||||
create table role
|
||||
(
|
||||
id bigint primary key,
|
||||
|
||||
name text not null unique,
|
||||
authority text not null unique
|
||||
);
|
||||
|
||||
-- COMMENTS
|
||||
comment on table role is 'Таблица ролей пользователей';
|
||||
|
||||
comment on column role.name is 'Человекочитаемое имя роли';
|
||||
comment on column role.authority is 'Имя роли в системе';
|
||||
@ -0,0 +1,22 @@
|
||||
create table "user"
|
||||
(
|
||||
id bigserial primary key,
|
||||
|
||||
login text not null unique,
|
||||
password text not null,
|
||||
full_name text not null,
|
||||
email text not null unique,
|
||||
number_phone text not null unique,
|
||||
|
||||
created_at timestamptz not null,
|
||||
updated_at timestamptz
|
||||
);
|
||||
|
||||
-- COMMENTS
|
||||
comment on table "user" is 'Таблица пользователей';
|
||||
|
||||
comment on column "user".login is 'Логин пользователя';
|
||||
comment on column "user".password is 'Пароль пользователя';
|
||||
comment on column "user".full_name is 'Полное имя пользователя в формате Фамилия Имя Отчество';
|
||||
comment on column "user".email is 'Почта пользователя';
|
||||
comment on column "user".number_phone is 'Номер телефона пользователя';
|
||||
@ -0,0 +1,21 @@
|
||||
create table user_role
|
||||
(
|
||||
id bigserial primary key,
|
||||
|
||||
user_id bigint not null,
|
||||
role_id bigint not null
|
||||
);
|
||||
|
||||
-- FOREIGN KEY
|
||||
alter table user_role
|
||||
add constraint fk_user_role_user_id
|
||||
foreign key (user_id) references "user" (id);
|
||||
alter table user_role
|
||||
add constraint fk_user_role_role_id
|
||||
foreign key (role_id) references role (id);
|
||||
|
||||
-- COMMENTS
|
||||
comment on table user_role is 'Таблица связи пользователей и ролей';
|
||||
|
||||
comment on column user_role.user_id is 'Идентификатор пользователя';
|
||||
comment on column user_role.role_id is 'Идентификатор роли';
|
||||
@ -0,0 +1,11 @@
|
||||
create table diploma_topic
|
||||
(
|
||||
id bigserial primary key,
|
||||
|
||||
name text not null
|
||||
);
|
||||
|
||||
-- COMMENTS
|
||||
comment on table diploma_topic is 'Таблица тем дипломных работ';
|
||||
|
||||
comment on column diploma_topic.name is 'Название темы дипломной работы';
|
||||
@ -0,0 +1,22 @@
|
||||
create table "group"
|
||||
(
|
||||
id bigserial primary key,
|
||||
|
||||
name text not null unique,
|
||||
curator_user_id bigint,
|
||||
|
||||
created_at timestamptz not null,
|
||||
updated_at timestamptz
|
||||
);
|
||||
|
||||
-- FOREIGN KEY
|
||||
alter table "group"
|
||||
add constraint fk_group_curator_user_id
|
||||
foreign key (curator_user_id) references "user" (id)
|
||||
on delete set null on update cascade;
|
||||
|
||||
-- COMMENTS
|
||||
comment on table "group" is 'Таблица групп студентов';
|
||||
|
||||
comment on column "group".name is 'Название группы';
|
||||
comment on column "group".curator_user_id is 'Идентификатор куратора группы';
|
||||
@ -0,0 +1,65 @@
|
||||
create table student
|
||||
(
|
||||
id bigserial primary key,
|
||||
user_id bigint not null,
|
||||
diploma_topic_id bigint not null,
|
||||
mentor_user_id bigint not null,
|
||||
group_id bigint not null,
|
||||
|
||||
form boolean,
|
||||
protection_day int,
|
||||
protection_order int,
|
||||
magistracy text,
|
||||
digital_format_present boolean,
|
||||
mark_comment int,
|
||||
mark_practice int,
|
||||
predefence_comment text,
|
||||
normal_control text,
|
||||
anti_plagiarism int,
|
||||
note text,
|
||||
record_book_returned boolean,
|
||||
work text,
|
||||
|
||||
created_at timestamptz not null,
|
||||
updated_at timestamptz
|
||||
);
|
||||
|
||||
-- FOREIGN KEY
|
||||
alter table student
|
||||
add constraint fk_student_user_id
|
||||
foreign key (user_id) references "user" (id)
|
||||
on delete cascade on update cascade;
|
||||
alter table student
|
||||
add constraint fk_student_diploma_topic_id
|
||||
foreign key (diploma_topic_id) references diploma_topic (id)
|
||||
on delete set null on update cascade;
|
||||
alter table student
|
||||
add constraint fk_student_mentor_user_id
|
||||
foreign key (mentor_user_id) references "user" (id)
|
||||
on delete set null on update cascade;
|
||||
alter table student
|
||||
add constraint fk_student_group_id
|
||||
foreign key (group_id) references "group" (id)
|
||||
on delete set null on update cascade;
|
||||
|
||||
-- COMMENTS
|
||||
comment on table student is 'Таблица студентов';
|
||||
|
||||
comment on column student.user_id is 'Идентификатор пользователя';
|
||||
comment on column student.diploma_topic_id is 'Идентификатор темы дипломной работы';
|
||||
comment on column student.mentor_user_id is 'Идентификатор научного руководителя';
|
||||
comment on column student.group_id is 'Идентификатор группы';
|
||||
|
||||
comment on column student.form is 'Форма обучения';
|
||||
comment on column student.protection_day is 'День защиты';
|
||||
comment on column student.protection_order is 'Порядок защиты';
|
||||
comment on column student.magistracy is 'Магистратура';
|
||||
comment on column student.digital_format_present is 'Предоставлен в электронном виде';
|
||||
comment on column student.mark_comment is 'Комментарий к оценке';
|
||||
comment on column student.mark_practice is 'Оценка практики';
|
||||
comment on column student.predefence_comment is 'Комментарий к защите';
|
||||
comment on column student.normal_control is 'Обычный контроль';
|
||||
comment on column student.anti_plagiarism is 'Антиплагиат';
|
||||
comment on column student.note is 'Примечание';
|
||||
comment on column student.record_book_returned is 'Ведомость возвращена';
|
||||
comment on column student.work is 'Работа';
|
||||
@ -0,0 +1,6 @@
|
||||
insert into role (id, name, authority)
|
||||
values (1, 'Преподаватель', 'ROLE_TEACHER'),
|
||||
(2, 'Студент', 'ROLE_STUDENT'),
|
||||
(3, 'Член комиссии ГЭК', 'ROLE_COMMISSION_MEMBER'),
|
||||
(4, 'Администратор', 'ROLE_ADMINISTRATOR'),
|
||||
(5, 'Секретарь', 'ROLE_SECRETARY');
|
||||
@ -0,0 +1,8 @@
|
||||
insert into "user" (id, login, password, full_name, email, number_phone, created_at)
|
||||
values (1, 'admin', '{noop}admin', 'Администратор', 'admin@tdms.tu-byransk.ru', '', now());
|
||||
|
||||
insert into user_role (id, user_id, role_id)
|
||||
values (1, 1, 4);
|
||||
|
||||
select setval('user_id_seq', (select max(id) from "user"));
|
||||
select setval('user_role_id_seq', (select max(id) from user_role));
|
||||
@ -0,0 +1,7 @@
|
||||
create table defence
|
||||
(
|
||||
id bigserial primary key,
|
||||
defence_date timestamptz,
|
||||
created_at timestamptz not null,
|
||||
updated_at timestamptz
|
||||
);
|
||||
@ -1,4 +1,4 @@
|
||||
INSERT INTO vkr.diploma_topic (name)
|
||||
INSERT INTO diploma_topic (name)
|
||||
VALUES ('Мобильное приложение для заказа автозапчастей на платформе ABCP'),
|
||||
('Подсистема уведомления пользователей для программного комплекса "РискПроф. Учебный центр"'),
|
||||
('Веб-приложение "Таск-менеджер"'),
|
||||
3
server/src/main/resources/db/test-data/group.sql
Normal file
3
server/src/main/resources/db/test-data/group.sql
Normal file
@ -0,0 +1,3 @@
|
||||
INSERT INTO "group" (name, principal_user_id)
|
||||
VALUES ('ИВТ-1', 40),
|
||||
('ИВТ-2', 40);
|
||||
98
server/src/main/resources/db/test-data/student.sql
Normal file
98
server/src/main/resources/db/test-data/student.sql
Normal file
@ -0,0 +1,98 @@
|
||||
do
|
||||
$$
|
||||
begin
|
||||
INSERT INTO student (form,
|
||||
protection_order,
|
||||
magistracy,
|
||||
digital_format_present,
|
||||
mark_comment,
|
||||
mark_practice,
|
||||
predefence_comment,
|
||||
normal_control,
|
||||
anti_plagiarism,
|
||||
note,
|
||||
record_book_returned,
|
||||
work,
|
||||
user_id,
|
||||
diploma_topic_id,
|
||||
mentor_user_id,
|
||||
group_id,
|
||||
created_at)
|
||||
VALUES (true, 1100, 'ПРИ, ИВТ или другой вуз', true, 5, 5, 'ок', 'Подписано', 7862, 'Акт о внедрении', true,
|
||||
'нет', 1, 1, 40, 3, now()),
|
||||
(true, 1110, 'Да, но не уверен, в БГТУ ли', true, 5, 5, 'ок', 'Подписано', 8196,
|
||||
'Заявка, Акт о внедрении', true, 'ООО "ЦИРОБЗ"', 2, 2, 40, 3, now()),
|
||||
(true, 3500, 'Нет', true, 3, 3,
|
||||
'Критически низкий уровень. Допущен под ответственность руководителя ВКР', 'Подписано', 7141, 'Иниц',
|
||||
true, 'нет', 3, 3, 41, 3, now()),
|
||||
(true, 1800, 'Да, но не уверен, в БГТУ ли', true, 4, 5, 'Усилить работу', 'Подписано', 5381, 'Иниц',
|
||||
true, 'нет', 4, 4, 42, 3, now()),
|
||||
(true, 2100, 'ИВТ, ПРИ', true, 5, 5, 'ок', 'Подписано', 8146, 'Заявка, Акт о внедрении', true, 'нет', 5,
|
||||
5, 43, 3, now()),
|
||||
(true, 1100, 'ИВТ, ПРИ', true, 5, 5, 'ок', 'Подписано', 7965, 'Иниц', true, 'нет', 6, 6, 41, 3, now()),
|
||||
(true, 1200, 'нет', true, 5, 4, 'Усилить работу', 'Подписано', 8583, Null, true,
|
||||
'Газ Энерго Комплект (ГЭК)', 7, 7, 44, 3, now()),
|
||||
(true, 1700, 'Нет', true, 5, 5,
|
||||
'Критически низкий уровень. Допущен под ответственность руководителя ВКР', 'Подписано', 6647, Null,
|
||||
true, 'нет', 8, 8, 45, 3, now()),
|
||||
(true, 3300, 'ИВТ, ПРИ или другой вуз', true, 5, 5, 'Усилить работу', 'Подписано', 6741,
|
||||
'Заявка, Акт о внедрении', true, 'нет', 9, 9, 46, 3, now()),
|
||||
(true, 1200, 'ИВТ', true, 5, 5, 'ок', 'Подписано', 7645, 'Заявка, Акт о внедрении', true, 'нет', 10, 10,
|
||||
40, 3, now()),
|
||||
(true, 3700, 'нет', true, 3, 3,
|
||||
'Критически низкий уровень. Допущен под ответственность руководителя ВКР', '1', 3093, 'Иниц', true,
|
||||
'нет', 11, 11, 45, 3, now()),
|
||||
(true, 1900, 'Нет', true, 5, 5, 'ок', 'Подписано', 8175, 'Заявка, Акт о внедрении', true, 'нет', 12, 12,
|
||||
42, 3, now()),
|
||||
(true, 3200, 'ИВТ', true, 5, 5, 'Усилить работу', 'Подписано', 7805, 'Акт', true, 'нет', 13, 13, 46, 3,
|
||||
now()),
|
||||
(true, 1200, 'ИВТ, ПРИ', true, 4, 4, 'ок', 'рек', 7590, 'Иниц', true, 'нет', 14, 14, 45, 3, now()),
|
||||
(true, 3900, 'нет', true, 5, 5, 'Усилить работу', 'Подписано', 6463, 'Заявка, Акт о внедрении', true,
|
||||
'нет', 15, 15, 40, 3, now()),
|
||||
(true, 2200, 'ИВТ', true, 5, 5, 'ок', 'Подписано', 7441, Null, true, 'ООО "ЦИРОБЗ"', 16, 16, 44, 3,
|
||||
now()),
|
||||
(true, 1130, 'Нет', true, 5, 4,
|
||||
'Критически низкий уровень. Допущен под ответственность руководителя ВКР', 'Подписано', 7319,
|
||||
'Заявка, Акт о внедрении', true, 'нет', 17, 17, 40, 3, now()),
|
||||
(true, 2400, 'ИВТ', true, 4, 5, 'ок', 'Подписано', 6436, Null, true, 'нет', 18, 18, 45, 3, now()),
|
||||
(true, 1600, 'ИВТ', true, 5, 5, 'Усилить работу', 'Подписано', 6227, 'Исслед', true, 'АО "БЭМЗ"', 19, 19,
|
||||
47, 3, now()),
|
||||
(true, 1400, 'Нет', true, 5, 5, 'ок', 'Подписано', 8935, 'Иниц', true, 'ООО "ЦИРОБЗ"', 20, 20, 44, 4,
|
||||
now()),
|
||||
(true, 2900, 'ИВТ', true, 5, 5, 'ок', 'Подписано', 7971, 'Заявка, Акт о внедрении', true, 'нет', 21, 21,
|
||||
40, 4, now()),
|
||||
(true, 2600, 'ИВТ, ПРИ', true, 3, 3, 'ок', 'Подписано', 7284, Null, true, 'нет', 22, 22, 48, 4, now()),
|
||||
(true, 3100, 'ИВТ, ПРИ', true, 5, 5, 'Усилить работу', 'Подписано', 4966, 'Заявка, Акт о внедрении',
|
||||
true, 'нет', 23, 23, 45, 4, now()),
|
||||
(true, 3110, 'Нет', true, 5, 5, 'ок', 'Подписано', 7174, Null, true, 'нет', 24, 24, 40, 4, now()),
|
||||
(true, 3800, 'Нет', true, 5, 5, 'ок', 'Подписано', 7233, Null, true, 'нет', 25, 25, 45, 4, now()),
|
||||
(true, 3300, 'ИВТ', true, 5, 5, 'ок', 'Подписано', 7133, Null, true, 'нет', 26, 26, 43, 4, now()),
|
||||
(true, 3130, 'ИВТ', true, 5, 5, 'ок', 'Подписано', 8083, 'Заявка, Акт о внедрении', true, 'нет', 27, 27,
|
||||
49, 4, now()),
|
||||
(true, 3400, 'Нет', true, 5, 4,
|
||||
'Критически низкий уровень. Допущен под ответственность руководителя ВКР', 'Подписано', 7968, 'Иниц',
|
||||
true, 'нет', 28, 28, 50, 4, now()),
|
||||
(true, 3120, 'ИВТ или ПРИ', true, 5, 5, 'ок', 'Подписано', 7940, 'Исслед', true, 'ООО "ЦИРОБЗ"', 29, 29,
|
||||
40, 4, now()),
|
||||
(true, 2800, 'Нет (возможно)', true, 4, 4, 'Усилить работу', 'рек', 6775, 'Заявка, Акт о внедрении',
|
||||
true, 'нет', 30, 30, 40, 4, now()),
|
||||
(true, 2100, 'Нет (возможно)', true, 5, 5, 'ок', 'Подписано', 7637, 'Заявка, Акт о внедрении', true,
|
||||
'нет', 31, 31, 40, 4, now()),
|
||||
(true, 2700, 'ИВТ', true, 5, 5, 'ок', 'Подписано', 8544, 'Заявка, Акт о внедрении', true, 'нет', 32, 32,
|
||||
45, 4, now()),
|
||||
(true, 2130, 'ИВТ, ПРИ', true, 5, 5, 'ок', 'Подписано', 7166, 'Заявка, Акт о внедрении', true, 'нет', 33,
|
||||
33, 51, 4, now()),
|
||||
(true, 2110, 'ИВТ', true, 5, 5, 'ок', 'Подписано', 6075, 'Заявка, Акт о внедрении', true, 'нет', 34, 34,
|
||||
52, 4, now()),
|
||||
(true, 3100, 'ИВТ', true, 5, 5, 'ок', 'Подписано', 7057, 'Заявка, Акт о внедрении', true, 'нет', 35, 35,
|
||||
50, 4, now()),
|
||||
(true, 2120, 'В БГТУ на другой кафедре (38.04.01 Экономика или 27.04.05. Инноватика)', true, 5, 5, 'ок',
|
||||
'Подписано', 7057, 'Заявка, Акт о внедрении', true, 'нет', 36, 36, 51, 4, now()),
|
||||
(true, 2500, 'ИВТ', true, 5, 5, 'ок', 'Подписано', 6583, 'Заявка, Акт о внедрении', true, 'нет', 37, 37,
|
||||
53, 4, now()),
|
||||
(true, 1300, 'ИВТ, ПРИ', true, 5, 5, 'ок', 'Подписано', 8444, 'Заявка, Акт о внедрении', true, 'нет', 38,
|
||||
38, 44, 4, now()),
|
||||
(true, 3600, 'ИВТ', true, 5, 5, 'ок', 'Подписано', 7631, 'Заявка, Акт о внедрении', true, 'нет', 39, 39,
|
||||
45, 4, now());
|
||||
end
|
||||
$$;
|
||||
82
server/src/main/resources/db/test-data/user.sql
Normal file
82
server/src/main/resources/db/test-data/user.sql
Normal file
@ -0,0 +1,82 @@
|
||||
INSERT INTO "user" (login, password, full_name, mail, number_phone, created_at)
|
||||
VALUES ('akulenko_mikhail', '{noop}1', 'Акуленко Михаил Вячеславович', 'akulenko.mikhail@example.com',
|
||||
'+79110000001', NOW()),
|
||||
('borovikov_artem', '{noop}1', 'Боровиков Артём Викторович', 'borovikov.artem@example.com', '+79110000002',
|
||||
NOW()),
|
||||
('bykonya_alexey', '{noop}1', 'Быконя Алексей Николаевич', 'bykonya.alexey@example.com', '+79110000003',
|
||||
NOW()),
|
||||
('ermakov_alexander', '{noop}1', 'Ермаков Александр Сергеевич', 'ermakov.alexander@example.com',
|
||||
'+79110000004', NOW()),
|
||||
('zgursky_evgeny', '{noop}1', 'Згурский Евгений Олегович', 'zgursky.evgeny@example.com', '+79110000005',
|
||||
NOW()),
|
||||
('ibishov_tural', '{noop}1', 'Ибишов Турал Садай оглы', 'ibishov.tural@example.com', '+79110000006', NOW()),
|
||||
('ignatenko_vladimir', '{noop}1', 'Игнатенко Владимир Алексеевич', 'ignatenko.vladimir@example.com',
|
||||
'+79110000007', NOW()),
|
||||
('lazukin_danila', '{noop}1', 'Лазукин Данила Дмитриевич', 'lazukin.danila@example.com', '+79110000008',
|
||||
NOW()),
|
||||
('mitiaev_danila', '{noop}1', 'Митяев Данила Алексеевич', 'mitiaev.danila@example.com', '+79110000009',
|
||||
NOW()),
|
||||
('neshkov_daniil', '{noop}1', 'Нешков Даниил Владимирович', 'neshkov.daniil@example.com', '+79110000010',
|
||||
NOW()),
|
||||
('petrov_pavel', '{noop}1', 'Петров Павел Сергеевич', 'petrov.pavel@example.com', '+79110000011', NOW()),
|
||||
('sazonov_andrey', '{noop}1', 'Сазонов Андрей Андреевич', 'sazonov.andrey@example.com', '+79110000012',
|
||||
NOW()),
|
||||
('solokhin_maxim', '{noop}1', 'Солохин Максим Николаевич', 'solokhin.maxim@example.com', '+79110000013',
|
||||
NOW()),
|
||||
('sochinsky_artem', '{noop}1', 'Сочинский Артем Александрович', 'sochinsky.artem@example.com',
|
||||
'+79110000014', NOW()),
|
||||
('trisvyatsky_kirill', '{noop}1', 'Трисвятский Кирилл Андреевич', 'trisvyatsky.kirill@example.com',
|
||||
'+79110000015', NOW()),
|
||||
('turov_alexander', '{noop}1', 'Туров Александр Сергеевич', 'turov.alexander@example.com', '+79110000016',
|
||||
NOW()),
|
||||
('shevtsova_alexandra', '{noop}1', 'Шевцова Александра Валерьевна', 'shevtsova.alexandra@example.com',
|
||||
'+79110000017', NOW()),
|
||||
('kibalyuk_artem', '{noop}1', 'Кибалюк Артем Сергеевич', 'kibalyuk.artem@example.com', '+79110000018', NOW()),
|
||||
('shulindin_artem', '{noop}1', 'Шулындин Артём Андреевич', 'shulindin.artem@example.com', '+79110000019',
|
||||
NOW()),
|
||||
('belyaev_egor', '{noop}1', 'Беляев Егор Андреевич', 'belyaev.egor@example.com', '+79110000020', NOW()),
|
||||
('berezhnoy_igor', '{noop}1', 'Бережной Игорь Александрович', 'berezhnoy.igor@example.com', '+79110000021',
|
||||
NOW()),
|
||||
('bogun_pavel', '{noop}1', 'Богун Павел Сергеевич', 'bogun.pavel@example.com', '+79110000022', NOW()),
|
||||
('vaseykin_nikita', '{noop}1', 'Васейкин Никита Павлович', 'vaseykin.nikita@example.com', '+79110000023',
|
||||
NOW()),
|
||||
('gomonov_nikita', '{noop}1', 'Гомонов Никита Алексеевич', 'gomonov.nikita@example.com', '+79110000024',
|
||||
NOW()),
|
||||
('druyan_oleg', '{noop}1', 'Друян Олег Викторович', 'druyan.oleg@example.com', '+79110000025', NOW()),
|
||||
('ivanov_kirill', '{noop}1', 'Иванов Кирилл Эдуардович', 'ivanov.kirill@example.com', '+79110000026', NOW()),
|
||||
('ivanova_veronika', '{noop}1', 'Иванова Вероника Евгеньевна', 'ivanova.veronika@example.com',
|
||||
'+79110000027', NOW()),
|
||||
('izotov_ivan', '{noop}1', 'Изотов Иван Алексеевич', 'izotov.ivan@example.com', '+79110000028', NOW()),
|
||||
('isakov_zahar', '{noop}1', 'Исаков Захар Александрович', 'isakov.zahar@example.com', '+79110000029', NOW()),
|
||||
('iskritsky_daniil', '{noop}1', 'Искрицкий Даниил Павлович', 'iskritsky.daniil@example.com', '+79110000030',
|
||||
NOW()),
|
||||
('linko_daria', '{noop}1', 'Линько Дарья Андреевна', 'linko.daria@example.com', '+79110000031', NOW()),
|
||||
('logutov_kirill', '{noop}1', 'Логутов Кирилл Александрович', 'logutov.kirill@example.com', '+79110000032',
|
||||
NOW()),
|
||||
('nekrassov_sergey', '{noop}1', 'Некрасов Сергей Игоревич', 'nekrassov.sergey@example.com', '+79110000033',
|
||||
NOW()),
|
||||
('sinyagin_ilya', '{noop}1', 'Синягин Илья Александрович', 'sinyagin.ilya@example.com', '+79110000034',
|
||||
NOW()),
|
||||
('sopriko_daniil', '{noop}1', 'Соприко Даниил Сергеевич', 'sopriko.daniil@example.com', '+79110000035',
|
||||
NOW()),
|
||||
('turovsky_ivan', '{noop}1', 'Туровский Иван Алексеевич', 'turovsky.ivan@example.com', '+79110000036', NOW()),
|
||||
('frantsev_sergey', '{noop}1', 'Францев Сергей Дмитриевич', 'frantsev.sergey@example.com', '+79110000037',
|
||||
NOW()),
|
||||
('chepurnoy_maxim', '{noop}1', 'Чепурной Максим Романович', 'chepurnoy.maxim@example.com', '+79110000038',
|
||||
NOW()),
|
||||
('schemelinin_dmitry', '{noop}1', 'Щемелинин Дмитрий Михайлович', 'schemelinin.dmitry@example.com',
|
||||
'+79110000039', NOW()),
|
||||
('bulatitsky_d_i_1', '{noop}1', 'Булатицкий Д. И.', 'bulatitsky.d.i.1@example.com', '+79110000040', NOW()),
|
||||
('kopeliovich_d_i_1', '{noop}1', 'Копелиович Д. И.', 'kopeliovich.d.i.1@example.com', '+79110000041', NOW()),
|
||||
('dergachev_k_v', '{noop}1', 'Дергачев К. В.', 'dergachev.k.v@example.com', '+79110000042', NOW()),
|
||||
('trubakov_e_o', '{noop}1', 'Трубаков Е. О.', 'trubakov.e.o@example.com', '+79110000043', NOW()),
|
||||
('radchenko_a_o', '{noop}1', 'Радченко А. О.', 'radchenko.a.o@example.com', '+79110000044', NOW()),
|
||||
('zimin_s_n_1', '{noop}1', 'Зимин С. Н.', 'zimin.s.n.1@example.com', '+79110000045', NOW()),
|
||||
('koptenok_e_v', '{noop}1', 'Коптенок Е. В.', 'koptenok.e.v@example.com', '+79110000046', NOW()),
|
||||
('mikhaleva_o_a', '{noop}1', 'Михалева О. А.', 'mikhaleva.o.a@example.com', '+79110000047', NOW()),
|
||||
('gulakov_k_v', '{noop}1', 'Гулаков К. В.', 'gulakov.k.v@example.com', '+79110000048', NOW()),
|
||||
('titarev_d_v', '{noop}1', 'Титарёв Д. В.', 'titarev.d.v@example.com', '+79110000049', NOW()),
|
||||
('izrailev_v_ya_1', '{noop}1', 'Израилев В. Я.', 'izrailev.v.ya.1@example.com', '+79110000050', NOW()),
|
||||
('podvesovsky_a_g_1', '{noop}1', 'Подвесовский А. Г.', 'podvesovsky.a.g.1@example.com', '+79110000051', NOW()),
|
||||
('trubakov_a_o', '{noop}1', 'Трубаков А. О.', 'trubakov.a.o@example.com', '+79110000059', NOW()),
|
||||
('lageriev_d_g', '{noop}1', 'Лагерев Д. Г.', 'lageriev.d.g@example.com', '+79110000052', NOW());
|
||||
58
server/src/main/resources/db/test-data/user_role.sql
Normal file
58
server/src/main/resources/db/test-data/user_role.sql
Normal file
@ -0,0 +1,58 @@
|
||||
do
|
||||
$$
|
||||
declare
|
||||
teacher_id bigint := 1;
|
||||
student_id bigint := 2;
|
||||
commission_member_id bigint := 3;
|
||||
administrator_id bigint := 4;
|
||||
secretary_id bigint := 5;
|
||||
begin
|
||||
INSERT INTO user_role (user_id, role_id)
|
||||
VALUES (1, student_id),
|
||||
(2, student_id),
|
||||
(3, student_id),
|
||||
(4, student_id),
|
||||
(5, student_id),
|
||||
(6, student_id),
|
||||
(7, student_id),
|
||||
(8, student_id),
|
||||
(9, student_id),
|
||||
(10, student_id),
|
||||
(11, student_id),
|
||||
(12, student_id),
|
||||
(13, student_id),
|
||||
(14, student_id),
|
||||
(15, student_id),
|
||||
(16, student_id),
|
||||
(17, student_id),
|
||||
(18, student_id),
|
||||
(19, student_id),
|
||||
(20, student_id),
|
||||
(21, student_id),
|
||||
(22, student_id),
|
||||
(23, student_id),
|
||||
(24, student_id),
|
||||
(25, student_id),
|
||||
(26, student_id),
|
||||
(27, student_id),
|
||||
(28, student_id),
|
||||
(29, student_id),
|
||||
(30, student_id),
|
||||
(31, student_id),
|
||||
(32, student_id),
|
||||
(33, student_id),
|
||||
(34, student_id),
|
||||
(35, student_id),
|
||||
(36, student_id),
|
||||
(37, teacher_id),
|
||||
(37, administrator_id),
|
||||
(38, commission_member_id),
|
||||
(39, teacher_id),
|
||||
(40, teacher_id),
|
||||
(41, teacher_id),
|
||||
(42, teacher_id),
|
||||
(43, teacher_id),
|
||||
(44, secretary_id),
|
||||
(45, secretary_id);
|
||||
end
|
||||
$$;
|
||||
22
server/src/main/resources/logback.xml
Normal file
22
server/src/main/resources/logback.xml
Normal file
@ -0,0 +1,22 @@
|
||||
<configuration>
|
||||
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
|
||||
<file>logs/app.log</file>
|
||||
<append>false</append>
|
||||
<encoder>
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<logger name="ru.tubryansk.tdms" level="debug" />
|
||||
|
||||
<root level="warn">
|
||||
<appender-ref ref="CONSOLE" />
|
||||
<appender-ref ref="FILE" />
|
||||
</root>
|
||||
</configuration>
|
||||
@ -1,18 +0,0 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: { browser: true, es2020: true },
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:react-hooks/recommended',
|
||||
],
|
||||
ignorePatterns: ['dist', '.eslintrc.cjs'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['react-refresh'],
|
||||
rules: {
|
||||
'react-refresh/only-export-components': [
|
||||
'warn',
|
||||
{ allowConstantExport: true },
|
||||
],
|
||||
},
|
||||
}
|
||||
12
web/babel.config.json
Normal file
12
web/babel.config.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"presets": [
|
||||
["@babel/preset-env", {"modules": false}],
|
||||
"@babel/preset-react",
|
||||
"@babel/preset-typescript"
|
||||
],
|
||||
"plugins": [
|
||||
["@babel/plugin-proposal-decorators", { "legacy": true }],
|
||||
["@babel/plugin-proposal-class-properties"],
|
||||
["@babel/plugin-transform-typescript"]
|
||||
]
|
||||
}
|
||||
7294
web/package-lock.json
generated
7294
web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,35 +1,43 @@
|
||||
{
|
||||
"name": "web",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc -b && vite build",
|
||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||
"preview": "vite preview"
|
||||
"build": "webpack --mode production",
|
||||
"dev": "webpack-dev-server --mode development"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-buddy/ide-toolbox": "^2.4.0",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.6.0",
|
||||
"@fortawesome/free-brands-svg-icons": "^6.6.0",
|
||||
"@fortawesome/free-regular-svg-icons": "^6.6.0",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.6.0",
|
||||
"@fortawesome/react-fontawesome": "^0.2.2",
|
||||
"@types/lodash": "^4.17.15",
|
||||
"axios": "^1.7.7",
|
||||
"bootstrap": "^5.3.3",
|
||||
"lodash": "^4.17.21",
|
||||
"mobx": "^6.13.1",
|
||||
"mobx-react": "^9.1.1",
|
||||
"mobx-state-router": "^6.0.1",
|
||||
"react": "^18.3.1",
|
||||
"react": "^18.2.0",
|
||||
"react-bootstrap": "^2.10.4",
|
||||
"react-dom": "^18.3.1"
|
||||
"react-dom": "^18.2.0",
|
||||
"uuid": "^11.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "^7.15.0",
|
||||
"@typescript-eslint/parser": "^7.15.0",
|
||||
"@vitejs/plugin-react": "^4.3.1",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-react-hooks": "^4.6.2",
|
||||
"eslint-plugin-react-refresh": "^0.4.7",
|
||||
"typescript": "^5.2.2",
|
||||
"vite": "^5.3.4"
|
||||
"@babel/plugin-proposal-decorators": "^7.25.7",
|
||||
"@babel/preset-env": "^7.25.8",
|
||||
"@babel/preset-react": "^7.25.7",
|
||||
"@types/react": "^18.2.0",
|
||||
"@types/react-dom": "^18.2.0",
|
||||
"@types/webpack": "^5.28.5",
|
||||
"css-loader": "^7.1.2",
|
||||
"html-webpack-plugin": "^5.6.2",
|
||||
"style-loader": "^4.0.0",
|
||||
"ts-loader": "^9.5.1",
|
||||
"typescript": "^5.6.3",
|
||||
"webpack": "^5.95.0",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"webpack-dev-server": "^5.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,20 +1,17 @@
|
||||
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";
|
||||
import {initApp} from "./utils/init";
|
||||
import {RootStoreContext} from './store/RootStoreContext';
|
||||
import {viewMap} from "./router/viewMap";
|
||||
|
||||
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>
|
||||
<RootStoreContext.Provider value={rootStore}>
|
||||
<RouterContext.Provider value={rootStore.routerStore}>
|
||||
<RouterView viewMap={viewMap}/>
|
||||
</RouterContext.Provider>
|
||||
</RootStoreContext.Provider>
|
||||
);
|
||||
|
||||
89
web/src/components/NotificationContainer.tsx
Normal file
89
web/src/components/NotificationContainer.tsx
Normal file
@ -0,0 +1,89 @@
|
||||
import {ComponentContext} from "../utils/ComponentContext";
|
||||
import {observer} from "mobx-react";
|
||||
import {Notification, NotificationType} from "../store/NotificationStore";
|
||||
import {Card, CardBody, CardHeader, CardText, CardTitle, Col, Row} from "react-bootstrap";
|
||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||
import {action, makeObservable} from "mobx";
|
||||
|
||||
@observer
|
||||
export class NotificationContainer extends ComponentContext {
|
||||
forEachNotificationRender(notifications: Notification[], type: NotificationType) {
|
||||
return notifications.map(notification => (
|
||||
<NotificationPopup key={notification.uuid} notification={notification} type={type}/>
|
||||
));
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div style={{position: 'fixed', left: '50%', transform: 'translateX(-50%)', zIndex: 1000}}>
|
||||
{this.forEachNotificationRender(this.notificationStore.errors, NotificationType.ERROR)}
|
||||
{this.forEachNotificationRender(this.notificationStore.successes, NotificationType.SUCCESS)}
|
||||
{this.forEachNotificationRender(this.notificationStore.warnings, NotificationType.WARNING)}
|
||||
{this.forEachNotificationRender(this.notificationStore.infos, NotificationType.INFO)}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
class NotificationPopup extends ComponentContext<{ notification: Notification, type: NotificationType }> {
|
||||
|
||||
constructor(props: { notification: Notification, type: NotificationType }) {
|
||||
super(props);
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
@action.bound
|
||||
close() {
|
||||
this.notificationStore.close(this.props.notification.uuid);
|
||||
}
|
||||
|
||||
get cardClassName() {
|
||||
switch (this.props.type) {
|
||||
case NotificationType.ERROR:
|
||||
return 'text-bg-danger';
|
||||
case NotificationType.WARNING:
|
||||
return 'text-bg-warning';
|
||||
case NotificationType.INFO:
|
||||
return 'text-bg-info';
|
||||
case NotificationType.SUCCESS:
|
||||
return 'text-bg-success';
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const hasTitle = !!this.props.notification.title && this.props.notification.title.length > 0;
|
||||
const closeIcon = <FontAwesomeIcon icon={'close'} onClick={this.close}/>;
|
||||
|
||||
return <Card className={`position-relative mt-3 opacity-75 ${this.cardClassName}`}>
|
||||
{
|
||||
hasTitle &&
|
||||
<CardHeader>
|
||||
<CardTitle>
|
||||
<Row>
|
||||
<Col sm={11}>
|
||||
{this.props.notification.title}
|
||||
</Col>
|
||||
<Col className={'text-end'}>
|
||||
{closeIcon}
|
||||
</Col>
|
||||
</Row>
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
}
|
||||
<CardBody>
|
||||
<CardText>
|
||||
<Row>
|
||||
<Col sm={11}>
|
||||
{this.props.notification.message}
|
||||
</Col>
|
||||
{
|
||||
!hasTitle &&
|
||||
<Col className={'text-end'}>
|
||||
{closeIcon}
|
||||
</Col>
|
||||
}
|
||||
</Row>
|
||||
</CardText>
|
||||
</CardBody>
|
||||
</Card>
|
||||
}
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
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/>
|
||||
</>
|
||||
}
|
||||
}
|
||||
@ -1,21 +0,0 @@
|
||||
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;
|
||||
@ -1,49 +0,0 @@
|
||||
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;
|
||||
@ -1,7 +0,0 @@
|
||||
import {DefaultPage} from "./DefaultPage.tsx";
|
||||
|
||||
export default class Root extends DefaultPage {
|
||||
get page() {
|
||||
return <h1>Home</h1>
|
||||
}
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
import {DefaultPage} from "./DefaultPage.tsx";
|
||||
|
||||
export default class UserProfile extends DefaultPage {
|
||||
get page() {
|
||||
return <h1>User Profile</h1>
|
||||
}
|
||||
}
|
||||
147
web/src/components/custom/DataTable.tsx
Normal file
147
web/src/components/custom/DataTable.tsx
Normal file
@ -0,0 +1,147 @@
|
||||
import {ComponentContext} from "../../utils/ComponentContext";
|
||||
import {TableDescriptor} from "../../utils/tables";
|
||||
import {observer} from "mobx-react";
|
||||
import {action, makeObservable} from "mobx";
|
||||
import {FormSelect, Pagination, Table} from "react-bootstrap";
|
||||
|
||||
export interface DataTableProps<T> {
|
||||
tableDescriptor: TableDescriptor<T>;
|
||||
}
|
||||
|
||||
@observer
|
||||
export class DataTable<T> extends ComponentContext<DataTableProps<T>> {
|
||||
constructor(props: DataTableProps<T>) {
|
||||
super(props);
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
header() {
|
||||
return <tr>
|
||||
{this.props.tableDescriptor.columns.map(column => <th className={'text-center'}
|
||||
key={column.key}>{column.title}</th>)}
|
||||
</tr>
|
||||
}
|
||||
|
||||
body() {
|
||||
const firstColumnKey = this.props.tableDescriptor.columns[0].key;
|
||||
return this.props.tableDescriptor.data.map(row => {
|
||||
const rowAny = row as any;
|
||||
return <tr key={rowAny[firstColumnKey]}>
|
||||
{
|
||||
this.props.tableDescriptor.columns.map(column => {
|
||||
return <td
|
||||
className={'text-center'}
|
||||
key={column.key}>
|
||||
{column.format(rowAny[column.key])}
|
||||
</td>
|
||||
})
|
||||
}
|
||||
</tr>
|
||||
});
|
||||
}
|
||||
|
||||
isFirstPage() {
|
||||
if (typeof this.props.tableDescriptor.page === 'undefined') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.props.tableDescriptor.page === 0;
|
||||
}
|
||||
|
||||
isLastPage() {
|
||||
if (typeof this.props.tableDescriptor.page === 'undefined' || typeof this.props.tableDescriptor.pageSize === 'undefined') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.props.tableDescriptor.page === (this.props.tableDescriptor.data.length / this.props.tableDescriptor.pageSize);
|
||||
}
|
||||
|
||||
@action.bound
|
||||
goFirstPage() {
|
||||
if (typeof this.props.tableDescriptor.page === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
this.props.tableDescriptor.page = 0;
|
||||
}
|
||||
|
||||
@action.bound
|
||||
goLastPage() {
|
||||
if (typeof this.props.tableDescriptor.page === 'undefined' || typeof this.props.tableDescriptor.pageSize === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
this.props.tableDescriptor.page = this.props.tableDescriptor.data.length / this.props.tableDescriptor.pageSize;
|
||||
}
|
||||
|
||||
@action.bound
|
||||
goNextPage() {
|
||||
if (typeof this.props.tableDescriptor.page === 'undefined' || typeof this.props.tableDescriptor.pageSize === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
this.props.tableDescriptor.page++;
|
||||
}
|
||||
|
||||
@action.bound
|
||||
goPrevPage() {
|
||||
if (typeof this.props.tableDescriptor.page === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
this.props.tableDescriptor.page--;
|
||||
}
|
||||
|
||||
@action.bound
|
||||
changePageSize(e: any) {
|
||||
this.props.tableDescriptor.pageSize = parseInt(e.target.value);
|
||||
}
|
||||
|
||||
footer() {
|
||||
const table = this.props.tableDescriptor;
|
||||
if (typeof table.page === 'undefined' || typeof table.pageSize === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <tr className={'text-center'}>
|
||||
<td colSpan={table.columns.length}>
|
||||
<div className={'d-flex justify-content-between'}>
|
||||
<div/>
|
||||
<Pagination className={'mb-0'}>
|
||||
<Pagination.First onClick={this.goFirstPage} disabled={this.isFirstPage()}/>
|
||||
<Pagination.Ellipsis disabled={this.isFirstPage()}/>
|
||||
<Pagination.Prev onClick={this.goPrevPage} disabled={this.isFirstPage()}/>
|
||||
<Pagination.Item active>{this.props.tableDescriptor.page}</Pagination.Item>
|
||||
<Pagination.Next onClick={this.goNextPage} disabled={!this.isLastPage()}/>
|
||||
<Pagination.Ellipsis disabled={!this.isLastPage()}/>
|
||||
<Pagination.Last onClick={this.goLastPage} disabled={!this.isLastPage()}/>
|
||||
</Pagination>
|
||||
<FormSelect className={'w-auto'} onChange={this.changePageSize}>
|
||||
<option>10</option>
|
||||
<option>20</option>
|
||||
<option>50</option>
|
||||
<option>100</option>
|
||||
</FormSelect>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
|
||||
render() {
|
||||
const table = this.props.tableDescriptor;
|
||||
return <Table hover striped>
|
||||
<thead>
|
||||
{this.header()}
|
||||
</thead>
|
||||
<tbody>
|
||||
{this.body()}
|
||||
</tbody>
|
||||
{
|
||||
table.pageable &&
|
||||
<tfoot>
|
||||
{this.footer()}
|
||||
</tfoot>
|
||||
}
|
||||
</Table>
|
||||
}
|
||||
}
|
||||
3
web/src/components/custom/controls/ReactiveControls.css
Normal file
3
web/src/components/custom/controls/ReactiveControls.css
Normal file
@ -0,0 +1,3 @@
|
||||
.l-no-bg label::after {
|
||||
background-color: rgba(0, 0, 0, 0) !important;
|
||||
}
|
||||
116
web/src/components/custom/controls/ReactiveControls.tsx
Normal file
116
web/src/components/custom/controls/ReactiveControls.tsx
Normal file
@ -0,0 +1,116 @@
|
||||
import React from "react";
|
||||
import {ReactiveValue} from "../../../utils/reactive/reactiveValue";
|
||||
import {observer} from "mobx-react";
|
||||
import {action, makeObservable, observable} from "mobx";
|
||||
import {Button, ButtonGroup, FloatingLabel, FormControl, FormText, ToggleButton} from "react-bootstrap";
|
||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||
import './ReactiveControls.css';
|
||||
|
||||
export interface ReactiveInputProps<T> {
|
||||
value: ReactiveValue<T>;
|
||||
label?: string;
|
||||
disabled?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
@observer
|
||||
export class StringInput extends React.Component<ReactiveInputProps<string>> {
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
makeObservable(this);
|
||||
if (this.props.value.value === undefined) {
|
||||
this.props.value.setAuto('');
|
||||
}
|
||||
this.props.value.setField(this.props.label);
|
||||
}
|
||||
|
||||
@action.bound
|
||||
onChange(event: React.ChangeEvent<HTMLInputElement>) {
|
||||
this.props.value.set(event.currentTarget.value);
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div className={'mb-1 l-no-bg'}>
|
||||
{/*todo: disable background-color for label*/}
|
||||
<FloatingLabel label={this.props.label} className={`${this.props.className} mt-0 mb-0`}>
|
||||
<FormControl type='text' placeholder={this.props.label} disabled={this.props.disabled}
|
||||
onChange={this.onChange} value={this.props.value.value}
|
||||
className={`${this.props.value.invalid ? 'bg-danger' : this.props.value.touched ? 'bg-success' : ''} bg-opacity-10`}/>
|
||||
</FloatingLabel>
|
||||
<FormText children={this.props.value.firstError} className={`text-danger mt-0 mb-0 d-block`}/>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
export class PasswordInput extends React.Component<ReactiveInputProps<string>> {
|
||||
@observable showPassword = false;
|
||||
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
makeObservable(this);
|
||||
if (this.props.value.value === undefined) {
|
||||
this.props.value.setAuto('');
|
||||
}
|
||||
this.props.value.setField(this.props.label);
|
||||
}
|
||||
|
||||
@action.bound
|
||||
onChange(event: React.ChangeEvent<HTMLInputElement>) {
|
||||
this.props.value.set(event.currentTarget.value);
|
||||
}
|
||||
|
||||
@action.bound
|
||||
toggleShowPassword() {
|
||||
this.showPassword = !this.showPassword;
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div className={'mb-1 l-no-bg'}>
|
||||
<div className={'d-flex justify-content-between align-items-center'}>
|
||||
<FloatingLabel label={this.props.label} className={`${this.props.className} w-100`}>
|
||||
<FormControl type={`${this.showPassword ? 'text' : 'password'}`} placeholder={this.props.label}
|
||||
disabled={this.props.disabled}
|
||||
className={`${this.props.value.invalid ? 'bg-danger' : this.props.value.touched ? 'bg-success' : ''} bg-opacity-10`}
|
||||
onChange={this.onChange} value={this.props.value.value}/>
|
||||
</FloatingLabel>
|
||||
<Button onClick={this.toggleShowPassword} variant={"outline-secondary"}>
|
||||
<FontAwesomeIcon icon={this.showPassword ? 'eye-slash' : 'eye'}/>
|
||||
</Button>
|
||||
</div>
|
||||
<FormText children={this.props.value.firstError} className={'text-danger d-block mt-0 mb-0'}/>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
export class SelectButtonInput extends React.Component<ReactiveInputProps<string>> {
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
makeObservable(this);
|
||||
if (this.props.value.value === undefined) {
|
||||
this.props.value.setAuto('');
|
||||
}
|
||||
this.props.value.setField(this.props.label);
|
||||
}
|
||||
|
||||
@action.bound
|
||||
onChange(event: React.ChangeEvent<HTMLInputElement>) {
|
||||
this.props.value.set(event.currentTarget.value);
|
||||
}
|
||||
|
||||
render() {
|
||||
return <>
|
||||
<ButtonGroup className={'d-block l-no-bg'}>
|
||||
<ToggleButton key={'admin'} value={'admin'} id={`radio-admin`} type="radio"
|
||||
variant={'outline-primary'} children={'Администратор'}
|
||||
checked={this.props.value.value === 'admin'} onChange={this.onChange}/>
|
||||
<ToggleButton key={'student'} id={`radio-student`} type="radio" value={'student'}
|
||||
variant={'outline-primary'}
|
||||
checked={this.props.value.value === 'student'} onChange={this.onChange}
|
||||
children={'Студент'}/>
|
||||
</ButtonGroup>
|
||||
<FormText children={this.props.value.firstError} className={'text-danger d-block'}/>
|
||||
</>
|
||||
}
|
||||
}
|
||||
40
web/src/components/layout/DefaultPage.tsx
Normal file
40
web/src/components/layout/DefaultPage.tsx
Normal file
@ -0,0 +1,40 @@
|
||||
import {ReactNode} from "react";
|
||||
import {Container} from "react-bootstrap";
|
||||
import Header from "./Header";
|
||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||
import {ComponentContext} from "../../utils/ComponentContext";
|
||||
import {NotificationContainer} from "../NotificationContainer";
|
||||
import {Footer} from "./Footer";
|
||||
|
||||
export abstract class DefaultPage extends ComponentContext {
|
||||
get page(): ReactNode {
|
||||
throw new Error('This is not abstract method, ' +
|
||||
'because mobx cant handle abstract methods. ' +
|
||||
'Please override this method in child class. ' +
|
||||
'Do not call it directly.');
|
||||
}
|
||||
|
||||
render() {
|
||||
const thinking = this.thinkStore.isThinking();
|
||||
|
||||
return <>
|
||||
<Header/>
|
||||
<Container className={"mt-5 mb-5"}>
|
||||
{
|
||||
thinking &&
|
||||
<div id='fullscreen-loader'>
|
||||
<FontAwesomeIcon icon='gear' size="4x" spin/>
|
||||
</div>
|
||||
}
|
||||
{
|
||||
!thinking &&
|
||||
<>
|
||||
<NotificationContainer/>
|
||||
{this.page}
|
||||
</>
|
||||
}
|
||||
</Container>
|
||||
<Footer/>
|
||||
</>
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,7 @@
|
||||
import {DefaultPage} from "./DefaultPage.tsx";
|
||||
import {observer} from "mobx-react";
|
||||
import {DefaultPage} from "./DefaultPage";
|
||||
|
||||
@observer
|
||||
export default class Error extends DefaultPage {
|
||||
get page() {
|
||||
return <h1>Error</h1>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user