From 015f2f516c326c7f6ed61ceafcc1f69929534081 Mon Sep 17 00:00:00 2001 From: vladp Date: Thu, 11 Dec 2025 20:53:28 +0700 Subject: [PATCH 1/6] delete need full name --- .../validator/RegRequestValidator.java | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/src/main/java/ru/soune/nocopy/handler/validator/RegRequestValidator.java b/src/main/java/ru/soune/nocopy/handler/validator/RegRequestValidator.java index 5b8bfb4..ef7559f 100644 --- a/src/main/java/ru/soune/nocopy/handler/validator/RegRequestValidator.java +++ b/src/main/java/ru/soune/nocopy/handler/validator/RegRequestValidator.java @@ -52,27 +52,6 @@ public class RegRequestValidator implements Validator { errors.rejectValue("fullName", "fullName.invalid.chars", "Name can only contain letters, spaces, hyphens and apostrophes"); } - - String[] nameParts = trimmedName.split("\\s+"); - if (nameParts.length < 2) { - errors.rejectValue("fullName", "fullName.missing.parts", - "Please enter both first and last name"); - } - - for (int i = 0; i < nameParts.length; i++) { - String part = nameParts[i]; - if (part.length() < 2 && part.length() > 0) { - errors.rejectValue("fullName", "fullName.part.too.short", - "Each name part must be at least 2 characters"); - break; - } - - if (!Character.isUpperCase(part.charAt(0))) { - errors.rejectValue("fullName", "fullName.capitalization", - "Each name part should start with capital letter"); - break; - } - } } private void validateCompanyName(String companyName, Errors errors) { From f1643709893a7e65ed5e974fdc6efbc0df2d6ac5 Mon Sep 17 00:00:00 2001 From: vladp Date: Fri, 12 Dec 2025 13:07:46 +0700 Subject: [PATCH 2/6] add check adress --- build.gradle | 8 ++- .../validator/RegRequestValidator.java | 70 +++++++------------ 2 files changed, 34 insertions(+), 44 deletions(-) diff --git a/build.gradle b/build.gradle index c6cb590..d42173a 100644 --- a/build.gradle +++ b/build.gradle @@ -29,13 +29,19 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.security:spring-security-crypto:6.5.3' implementation 'org.mapstruct:mapstruct:1.5.5.Final' + implementation 'commons-validator:commons-validator:1.7' + annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.5.Final' + annotationProcessor 'org.projectlombok:lombok' + compileOnly 'org.projectlombok:lombok' + developmentOnly 'org.springframework.boot:spring-boot-devtools' + runtimeOnly 'com.microsoft.sqlserver:mssql-jdbc' runtimeOnly 'com.mysql:mysql-connector-j' runtimeOnly 'org.postgresql:postgresql' - annotationProcessor 'org.projectlombok:lombok' + testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.mockito:mockito-core:5.3.1' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' diff --git a/src/main/java/ru/soune/nocopy/handler/validator/RegRequestValidator.java b/src/main/java/ru/soune/nocopy/handler/validator/RegRequestValidator.java index ef7559f..884bc89 100644 --- a/src/main/java/ru/soune/nocopy/handler/validator/RegRequestValidator.java +++ b/src/main/java/ru/soune/nocopy/handler/validator/RegRequestValidator.java @@ -1,10 +1,14 @@ package ru.soune.nocopy.handler.validator; +import org.apache.commons.validator.routines.DomainValidator; +import org.apache.commons.validator.routines.EmailValidator; import org.springframework.stereotype.Component; import org.springframework.validation.Errors; import org.springframework.validation.Validator; import ru.soune.nocopy.dto.RegRequest; +import java.net.InetAddress; +import java.net.UnknownHostException; import java.util.*; @Component @@ -89,65 +93,45 @@ public class RegRequestValidator implements Validator { private void validateEmail(String email, Errors errors) { if (email == null || email.trim().isEmpty()) { - errors.rejectValue("email", "email.is.empty", - "Email must not be empty"); + errors.rejectValue("email", "email.is.empty", "Email must not be empty"); return; } String trimmedEmail = email.trim().toLowerCase(); - if (trimmedEmail.length() > 128) { - errors.rejectValue("email", "email.too.long", - "Email must be less than 128 characters"); + if (trimmedEmail.length() > 254) { + errors.rejectValue("email", "email.too.long", "Email is too long"); return; } - if (!trimmedEmail.matches("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$")) { - errors.rejectValue("email", "email.invalid.format", - "Invalid email format"); + EmailValidator emailValidator = EmailValidator.getInstance(true, true); + if (!emailValidator.isValid(trimmedEmail)) { + errors.rejectValue("email", "email.invalid", "Invalid email address"); return; } String[] parts = trimmedEmail.split("@"); - if (parts.length != 2) { - return; - } + if (parts.length == 2) { + String domain = parts[1]; + DomainValidator domainValidator = DomainValidator.getInstance(); - String localPart = parts[0]; - String domain = parts[1]; - - if (localPart.length() > 64) { - errors.rejectValue("email", "email.local.too.long", - "Email username too long"); - } - - if (!localPart.matches("^[a-z0-9+_.-]+$")) { - errors.rejectValue("email", "email.local.invalid.chars", - "Email contains invalid characters"); - } - - validateEmailDomain(domain, errors); - } - - private void validateEmailDomain(String domain, Errors errors) { - if (domain.length() > 255) { - errors.rejectValue("email", "email.domain.too.long", - "Email domain too long"); - } - - String[] domainParts = domain.split("\\."); - if (domainParts.length < 2) { - errors.rejectValue("email", "email.domain.invalid", - "Invalid domain name"); - } - - String tld = domainParts[domainParts.length - 1]; - if (tld.length() < 2) { - errors.rejectValue("email", "email.tld.invalid", - "Invalid domain extension"); + if (!checkDomainExists(domain)) { + errors.rejectValue("email", "email.domain.not.found", + "Domain does not exist"); + } } } + private boolean checkDomainExists(String domain) { + try { + InetAddress.getByName(domain); + return true; + } catch (UnknownHostException e) { + return false; + } + } + + private void validatePhone(String phone, Errors errors) { if (phone == null || phone.trim().isEmpty()) { errors.rejectValue("phone", "phone.empty", From 07aad9f51ca6bd8161850d9e91b50e37649ee86f Mon Sep 17 00:00:00 2001 From: vladp Date: Sat, 13 Dec 2025 01:52:53 +0700 Subject: [PATCH 3/6] return good piplne --- infrastructure/jenkins/deploy-front.groovy | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/infrastructure/jenkins/deploy-front.groovy b/infrastructure/jenkins/deploy-front.groovy index e0a2094..f8869ce 100644 --- a/infrastructure/jenkins/deploy-front.groovy +++ b/infrastructure/jenkins/deploy-front.groovy @@ -96,7 +96,7 @@ pipeline { elif [ -f 'docker-compose.yml' ]; then docker-compose -f docker-compose.yml build else - echo 'Exception:don't found docker-compose' + echo 'Exception: docker-compose file not found' exit 1 fi " @@ -120,6 +120,9 @@ pipeline { sshpass -p '$SSH_PASS' ssh $SSH_USER@92.242.61.23 " cd /opt/deployments/frontend/${params.BRANCH} + # Проверяем и подключаем к сети app-network если нужно + docker network inspect app-network 2>/dev/null || echo 'Warning: app-network not found' + if [ -f 'docker-compose.yaml' ]; then docker-compose -f docker-compose.yaml up -d elif [ -f 'docker-compose.yml' ]; then @@ -150,16 +153,16 @@ pipeline { sh """ sshpass -p '$SSH_PASS' ssh $SSH_USER@92.242.61.23 " if docker ps --format '{{.Names}}' | grep -q 'no-copy-frontend'; then - echo 'Container start' + echo 'Container started' sleep 5 HTTP_CODE=\$(curl -s -o /dev/null -w '%{http_code}' http://localhost:2998 || echo '000') - echo 'HTTP код: ' \$HTTP_CODE + echo 'HTTP code: \$HTTP_CODE' - echo 'Frong start on http://92.242.61.23:2998' + echo 'Frontend available on http://92.242.61.23:2998' else - echo 'Exception: container don't start' + echo 'Exception: container did not start' docker ps -a | grep frontend exit 1 fi @@ -173,11 +176,11 @@ pipeline { post { success { - echo "Front branch ${params.BRANCH} completed" - echo "Use on http://92.242.61.23:2998" + echo "Front branch ${params.BRANCH} deployment completed" + echo "Frontend available on http://92.242.61.23:2998" } failure { - echo "Failed" + echo "Deployment failed" } } } \ No newline at end of file From fade754c7c42ba0955c67224a955a12f4d330e39 Mon Sep 17 00:00:00 2001 From: vladp Date: Tue, 16 Dec 2025 12:41:55 +0700 Subject: [PATCH 4/6] fix check email --- .../validator/RegRequestValidator.java | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/src/main/java/ru/soune/nocopy/handler/validator/RegRequestValidator.java b/src/main/java/ru/soune/nocopy/handler/validator/RegRequestValidator.java index 884bc89..f3dbda7 100644 --- a/src/main/java/ru/soune/nocopy/handler/validator/RegRequestValidator.java +++ b/src/main/java/ru/soune/nocopy/handler/validator/RegRequestValidator.java @@ -109,26 +109,6 @@ public class RegRequestValidator implements Validator { errors.rejectValue("email", "email.invalid", "Invalid email address"); return; } - - String[] parts = trimmedEmail.split("@"); - if (parts.length == 2) { - String domain = parts[1]; - DomainValidator domainValidator = DomainValidator.getInstance(); - - if (!checkDomainExists(domain)) { - errors.rejectValue("email", "email.domain.not.found", - "Domain does not exist"); - } - } - } - - private boolean checkDomainExists(String domain) { - try { - InetAddress.getByName(domain); - return true; - } catch (UnknownHostException e) { - return false; - } } From 6797a47df56ad2ec59817e0951deac1e649d342e Mon Sep 17 00:00:00 2001 From: vladp Date: Tue, 16 Dec 2025 16:43:20 +0700 Subject: [PATCH 5/6] check unique phone --- src/main/java/ru/soune/nocopy/dto/MessageCode.java | 1 + src/main/java/ru/soune/nocopy/entity/User.java | 1 + .../ru/soune/nocopy/handler/RegRequestHandler.java | 10 ++++++---- .../ru/soune/nocopy/repository/UserRepository.java | 2 ++ 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/main/java/ru/soune/nocopy/dto/MessageCode.java b/src/main/java/ru/soune/nocopy/dto/MessageCode.java index e5fd6e4..baa48be 100644 --- a/src/main/java/ru/soune/nocopy/dto/MessageCode.java +++ b/src/main/java/ru/soune/nocopy/dto/MessageCode.java @@ -3,6 +3,7 @@ package ru.soune.nocopy.dto; public enum MessageCode { SUCCESS(0, "Operation successful"), REG_EMAIL_EXISTS(1, "Email already registered"), + REG_EMAIL_OR_PHONE_EXISTS(1, "Email or phone already registered"), INVALID_FIELD(2, "Invalid field"), INVALID_JSON_BODY(2, "Invalid fields in JSON object"), MSG_ID_NOT_FOUND(4, "Message id not found"), diff --git a/src/main/java/ru/soune/nocopy/entity/User.java b/src/main/java/ru/soune/nocopy/entity/User.java index a4fba7d..0841027 100644 --- a/src/main/java/ru/soune/nocopy/entity/User.java +++ b/src/main/java/ru/soune/nocopy/entity/User.java @@ -42,6 +42,7 @@ public class User { private String password; @Size(min = 11, max = 14) + @Column(unique = true) private String phone; @Enumerated(EnumType.STRING) diff --git a/src/main/java/ru/soune/nocopy/handler/RegRequestHandler.java b/src/main/java/ru/soune/nocopy/handler/RegRequestHandler.java index 65bc887..349f0a7 100644 --- a/src/main/java/ru/soune/nocopy/handler/RegRequestHandler.java +++ b/src/main/java/ru/soune/nocopy/handler/RegRequestHandler.java @@ -33,13 +33,15 @@ public class RegRequestHandler implements RequestHandler { public BaseResponse handle(BaseRequest request) throws ValidationException { RegRequest regRequest = objectMapper.convertValue(request.getMessageBody(), RegRequest.class); - if (userRepository.existsByEmail(regRequest.getEmail())) { + if (userRepository.existsByEmail(regRequest.getEmail()) || userRepository.existsByPhone(regRequest.getPhone())) { RegAnswer regAnswer = new RegAnswer(); regAnswer.setFieldErrors(Arrays.asList(Map.of("email", regRequest.getEmail()))); + regAnswer.setFieldErrors(Arrays.asList(Map.of("phone", regRequest.getPhone()))); - throw new NotValidFieldException("User already exists with email: " + regRequest.getEmail(), - new BaseResponse(request.getMsgId(), MessageCode.REG_EMAIL_EXISTS.getCode(), - MessageCode.REG_EMAIL_EXISTS.getDescription(), regAnswer)); + throw new NotValidFieldException("User already exists with email:" + regRequest.getEmail() + " or phone: " + + regRequest.getPhone(), new BaseResponse(request.getMsgId(), + MessageCode.REG_EMAIL_OR_PHONE_EXISTS.getCode(), + MessageCode.REG_EMAIL_OR_PHONE_EXISTS.getDescription(), regAnswer)); } BindingResult bindingResult = new BeanPropertyBindingResult(regRequest, "regRequest"); diff --git a/src/main/java/ru/soune/nocopy/repository/UserRepository.java b/src/main/java/ru/soune/nocopy/repository/UserRepository.java index da4d7f9..f674629 100644 --- a/src/main/java/ru/soune/nocopy/repository/UserRepository.java +++ b/src/main/java/ru/soune/nocopy/repository/UserRepository.java @@ -1,5 +1,6 @@ package ru.soune.nocopy.repository; +import jakarta.validation.constraints.Size; import org.springframework.data.jpa.repository.JpaRepository; import ru.soune.nocopy.entity.User; @@ -8,4 +9,5 @@ import java.util.Optional; public interface UserRepository extends JpaRepository { Optional findByEmail(String email); boolean existsByEmail(String email); + boolean existsByPhone(@Size(min = 11, max = 14) String phone); } From 651b92cb59aae38f91cf6650e783cd158cceb210 Mon Sep 17 00:00:00 2001 From: vladp Date: Wed, 17 Dec 2025 15:40:38 +0700 Subject: [PATCH 6/6] fix job --- infrastructure/jenkins/deploy.groovy | 77 +++++++++++++++++++++++----- 1 file changed, 65 insertions(+), 12 deletions(-) diff --git a/infrastructure/jenkins/deploy.groovy b/infrastructure/jenkins/deploy.groovy index f19a80c..43b39ff 100644 --- a/infrastructure/jenkins/deploy.groovy +++ b/infrastructure/jenkins/deploy.groovy @@ -54,15 +54,26 @@ pipeline { echo 'Step 1: Checking current state...' # Смотрим что запущено - docker ps --filter 'name=postgres|app-backend' --format 'table {{.Names}}\\t{{.Status}}\\t{{.Ports}}' + docker ps --filter 'name=postgres|app-backend' --format 'table {{.Names}}\\t{{.Image}}\\t{{.Status}}\\t{{.Ports}}' - echo 'Step 2: Stopping only application (keeping DB)...' + echo 'Step 2: Force cleanup old application...' - # Останавливаем только приложение, БД оставляем работать + # Принудительно удаляем старый контейнер и образ docker stop app-backend 2>/dev/null || echo 'App not running' docker rm app-backend 2>/dev/null || echo 'App not found' + docker rmi app-backend:latest 2>/dev/null || echo 'Image not found' - # Проверяем что БД работает, если нет - запускаем + # Удаляем все образы app-backend + docker images --filter 'reference=app-backend*' -q | xargs -r docker rmi 2>/dev/null || echo 'No images to remove' + + echo 'Step 3: Verify copied files...' + echo 'Files in directory:' + ls -la + echo '' + echo 'Checking Java sources:' + find . -name '*.java' | head -2 + + echo 'Step 4: Check if PostgreSQL is running...' if ! docker ps | grep -q postgres; then echo 'Starting PostgreSQL...' docker-compose up -d db @@ -71,13 +82,37 @@ pipeline { echo 'PostgreSQL already running' fi - echo 'Step 3: Building new application image...' - docker build --no-cache -t app-backend:latest . + echo 'Step 5: Build application with forced rebuild...' + echo 'Checking Dockerfile exists:' + ls -la Dockerfile 2>/dev/null || echo 'Dockerfile not found, creating simple one' - echo 'Step 4: Starting application...' + # Если нет Dockerfile, создаем простой + if [ ! -f Dockerfile ]; then + echo 'Creating simple Dockerfile...' + cat > Dockerfile << 'EOF' +FROM eclipse-temurin:21-jre +WORKDIR /app +COPY . /app/ +CMD ["java", "-jar", "app.jar"] +EOF + fi + + # Собираем с подробным выводом + echo 'Building Docker image...' + docker build --no-cache --progress=plain -t app-backend:latest . 2>&1 | tail -50 + + echo 'Step 6: Verify new image...' + echo 'Current app-backend images:' + docker images | grep app-backend + + echo 'Image creation time:' + docker inspect app-backend:latest --format='{{.Created}}' 2>/dev/null || echo 'Cannot inspect image' + + echo 'Step 7: Starting application with alias...' docker run -d \\ --name app-backend \\ --network app-network \\ + --network-alias app \\ -p 80:8080 \\ -e POSTGRES_DB=no_copy_ \\ -e POSTGRES_USER=postgres \\ @@ -87,17 +122,35 @@ pipeline { --restart unless-stopped \\ app-backend:latest - echo 'Step 5: Checking deployment...' + echo 'Step 8: Checking deployment...' sleep 10 echo 'Containers status:' - docker ps --filter 'name=postgres|app-backend' --format 'table {{.Names}}\\t{{.Status}}\\t{{.Ports}}' + docker ps --filter 'name=postgres|app-backend' --format 'table {{.Names}}\\t{{.Image}}\\t{{.Status}}\\t{{.CreatedAt}}' - echo 'Application logs (last 5 lines):' - docker logs --tail=5 app-backend 2>/dev/null || echo 'Logs not available yet' + echo 'Checking image inside container:' + docker exec app-backend ls -la /app/ 2>/dev/null || echo 'Cannot check container files' + echo 'Application logs (last 10 lines):' + docker logs --tail=10 app-backend 2>/dev/null || echo 'Logs not available yet' + + echo 'Step 9: Health check...' if docker ps | grep -q app-backend; then - echo 'Deployment successful' + echo 'Container is running' + echo 'Testing application health...' + for i in {1..5}; do + if curl -s -f http://localhost:80/health > /dev/null 2>&1; then + echo 'Health check passed' + echo 'Deployment successful' + echo 'Application URL: http://${params.SERVER}:80' + exit 0 + fi + echo 'Waiting for application to start... attempt ' \$i + sleep 5 + done + + echo 'Health check failed, but container is running' + echo 'Application might be starting slowly' echo 'Application URL: http://${params.SERVER}:80' else echo 'Application failed to start'