diff --git a/.env b/.env index e54b26f..0c81544 100644 --- a/.env +++ b/.env @@ -1,10 +1,19 @@ +# Postgres POSTGRES_DB=no_copy_ POSTGRES_USER=ncp_db POSTGRES_PASSWORD=ncpDbApp POSTGRES_PORT=5432 POSTGRES_HOST=postgres +# Redis REDIS_HOST=redis REDIS_PORT=6379 -SERVER_PORT=8080 \ No newline at end of file +# Backend server +SERVER_PORT=8080 + +# Postfix +MAIL_HOST_PROD=postfix-production +MAIL_PORT_PROD=25 +MAIL_USERNAME_PROD=noreply@nocopy.com +SMTP_PASSWORD_PROD=nocopy!nocopy! \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml index 6a69921..a6a02d4 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,15 +1,32 @@ version: '3.9' services: + postfix: + image: boky/postfix:latest + container_name: postfix-production + hostname: ${DOMAIN:-no-copy.ru} + environment: + - DOMAIN=${DOMAIN:-no-copy.ru} + - ALLOWED_SENDER_DOMAINS=* + - RELAYHOST= + - DISABLE_SMTP_AUTH= + - SMTP_USERNAME=${MAIL_USERNAME:-noreply@no-copy.ru} + - SMTP_PASSWORD=${SMTP_PASSWORD:-nocopy!nocopy!} + - RELAY_NETWORKS=172.16.0.0/12 + - POSTFIX_mynetworks=127.0.0.0/8 172.17.0.0/16 172.16.0.0/12 + - POSTFIX_smtpd_relay_restrictions=permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination + - POSTFIX_smtpd_recipient_restrictions=permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination + - DISABLE_SIGNING=true + ports: + - "25:25" + - "587:587" + networks: + - app-network + restart: unless-stopped + storage: image: alpine:latest container_name: file-storage - deploy: - resources: - limits: - memory: 128M - reservations: - memory: 64M networks: - app-network volumes: @@ -19,19 +36,9 @@ services: db: image: postgres:17.7 - restart: always - deploy: - resources: - limits: - cpus: '2.0' - memory: 2G - reservations: - cpus: '1.0' - memory: 1G + container_name: postgres environment: POSTGRES_DB: no_copy_ -# POSTGRES_USER: postgres -# POSTGRES_PASSWORD: postgres POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} POSTGRES_SHARED_BUFFERS: 512MB @@ -41,42 +48,36 @@ services: volumes: - pgdata:/var/lib/postgresql/data - ./init-scripts:/docker-entrypoint-initdb.d:ro - container_name: postgres networks: app-network: aliases: - database + restart: unless-stopped app: build: . container_name: app-backend - deploy: - resources: - limits: - cpus: '1.5' - memory: 1G - reservations: - cpus: '0.5' - memory: 512M environment: FILE_STORAGE_PATH: /data/uploads MAX_FILE_SIZE: 10737418240 -# FILE_CHUNK_SIZE: 1048576 FILE_CHUNK_SIZE: 1000000 POSTGRES_DB: no_copy_ -# POSTGRES_USER: postgres -# POSTGRES_PASSWORD: postgres POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} POSTGRES_PORT: 5432 POSTGRES_HOST: db STORAGE_SERVICE_URL: http://storage:8081 SPRING_PROFILES_ACTIVE: docker - YANDEX_API_KEY: AQVNyaVaUmgUb1GMCtf5zSEqFxy0woXrcMOOB43q - YANDEX_FOLDER_ID: b1gokpdbm6qfpsou8pcd - YANDEX_SEARCH_URL: "https://searchapi.api.cloud.yandex.net/v2/image/search_by_image" + MAIL_HOST: postfix + MAIL_PORT: 25 + MAIL_USERNAME: noreply@no-copy.ru + SMTP_PASSWORD: nocopy!nocopy! + SPRING_MAIL_PROPERTIES_MAIL_SMTP_STARTTLS_ENABLE: "false" + SPRING_MAIL_PROPERTIES_MAIL_SMTP_AUTH: "false" + SPRING_MAIL_PROPERTIES_MAIL_DEBUG: "true" depends_on: - db + - postfix ports: - "80:8080" networks: @@ -88,163 +89,17 @@ services: volumes: - uploads_data:/data/uploads:rw healthcheck: - test: [ "CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d no_copy_" ] + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d no_copy_"] interval: 10s retries: 5 - - grafana: - image: grafana/grafana:10.3.1 - container_name: grafana - deploy: - resources: - limits: - cpus: '0.5' - memory: 512M - reservations: - memory: 256M - ports: - - "3000:3000" - depends_on: - prometheus: - condition: service_healthy - loki: - condition: service_healthy - tempo: - condition: service_started - alloy: - condition: service_started - environment: - GF_AUTH_ANONYMOUS_ENABLED: "true" - GF_SECURITY_ADMIN_USER: admin - GF_SECURITY_ADMIN_PASSWORD: admin - GF_METRICS_ENABLED: "true" - GF_DATABASE_MAX_IDLE_CONN: "2" - GF_DATABASE_MAX_OPEN_CONN: "10" - volumes: - - ./infrastructure/grafana/provisioning:/etc/grafana/provisioning - - ./infrastructure/grafana/dashboards:/var/lib/grafana/dashboards - healthcheck: - test: [ "CMD", "wget", "--spider", "http://localhost:3000/api/health" ] - interval: 10s - timeout: 5s - retries: 10 - networks: - - app-network - - prometheus: - image: prom/prometheus:latest - container_name: prometheus - deploy: - resources: - limits: - cpus: '0.5' - memory: 1G - reservations: - memory: 512M - ports: - - "9090:9090" - volumes: - - ./infrastructure/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml - - prometheus_data:/prometheus - command: - - '--config.file=/etc/prometheus/prometheus.yml' - - '--storage.tsdb.path=/prometheus' - - '--storage.tsdb.retention.time=15d' # Удерживать 15 дней - - '--web.enable-lifecycle' - healthcheck: - test: [ "CMD", "wget", "--spider", "http://localhost:9090/-/healthy" ] - interval: 10s - timeout: 5s - retries: 5 - networks: - - app-network - - loki: - image: grafana/loki:2.9.2 - container_name: loki - deploy: - resources: - limits: - cpus: '1.0' - memory: 2G - reservations: - memory: 1G - ports: - - "3100:3100" - volumes: - - ./infrastructure/loki/loki-config.yaml:/etc/loki/loki-config.yaml:ro - - loki_data:/loki - command: - - -config.file=/etc/loki/loki-config.yaml - healthcheck: - test: [ "CMD", "wget", "--spider", "-q", "http://localhost:3100/ready" ] - interval: 10s - timeout: 5s - retries: 5 - networks: - - app-network - - tempo: - image: grafana/tempo:2.4.1 - container_name: tempo - deploy: - resources: - limits: - cpus: '0.5' - memory: 512M - reservations: - memory: 256M - command: [ "-config.file=/etc/tempo/tempo.yaml" ] - volumes: - - ./infrastructure/tempo/tempo.yaml:/etc/tempo/tempo.yaml - - tempo_data:/var/tempo - ports: - - "3200:3200" - networks: - - app-network - - alloy: - image: grafana/alloy:latest - container_name: alloy - deploy: - resources: - limits: - cpus: '0.5' - memory: 256M - reservations: - memory: 128M - user: root - ports: - - "9080:9080" - - "4317:4317" - - "4318:4318" - volumes: - - ./infrastructure/alloy/config.alloy:/etc/alloy/config.alloy:ro - - /var/lib/docker/containers:/var/lib/docker/containers:ro - - /var/run/docker.sock:/var/run/docker.sock - - /var/log:/var/log:ro - environment: - GRAFANA_LOKI_URL: http://loki:3100/loki/api/v1/push - command: - - run - - --server.http.listen-addr=0.0.0.0:9080 - - --storage.path=/var/lib/alloy/data - - /etc/alloy/config.alloy - networks: - - app-network + restart: unless-stopped volumes: pgdata: - artifacts: - tempo_data: - loki_data: - loki_chunks: - loki_index: - loki_rules: uploads_data: - prometheus_data: + postfix-data: networks: app-network: external: true - driver: bridge \ No newline at end of file + driver: bridge diff --git a/src/main/java/ru/soune/nocopy/entity/user/User.java b/src/main/java/ru/soune/nocopy/entity/user/User.java index 830bafd..5a146fe 100644 --- a/src/main/java/ru/soune/nocopy/entity/user/User.java +++ b/src/main/java/ru/soune/nocopy/entity/user/User.java @@ -36,8 +36,8 @@ public class User { @Column(name = "email", nullable = false, length = 1024, unique = true) private String email; - @Column(name = "email_verified", nullable = false) - private Boolean emailVerified = false; + @Column(name = "email_verified") + private boolean emailVerified = false; @Column(name = "company") private String company; @@ -82,11 +82,6 @@ public class User { @ToString.Exclude private List userContents = new ArrayList<>(); - @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) - @JsonIgnore - @ToString.Exclude - private List userActions = new ArrayList<>(); - @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) @JsonIgnore @ToString.Exclude diff --git a/src/main/java/ru/soune/nocopy/entity/user/UserAction.java b/src/main/java/ru/soune/nocopy/entity/user/UserAction.java deleted file mode 100644 index 9a27e8e..0000000 --- a/src/main/java/ru/soune/nocopy/entity/user/UserAction.java +++ /dev/null @@ -1,44 +0,0 @@ -package ru.soune.nocopy.entity.user; - -import jakarta.persistence.*; -import lombok.*; -import org.springframework.data.annotation.CreatedDate; -import org.springframework.data.jpa.domain.support.AuditingEntityListener; - -import java.time.LocalDateTime; - -@Entity -@Table(name = "user_actions") -@Getter -@Setter -@NoArgsConstructor -@AllArgsConstructor -@ToString -@EntityListeners(AuditingEntityListener.class) -public class UserAction { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long actionId; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "user_id", nullable = false) - @ToString.Exclude - private User user; - - @Column(name = "action", nullable = false, length = 100) - private String action; - - @Column(name = "details", columnDefinition = "TEXT") - private String details; - - @Column(name = "ip_address", length = 45) - private String ipAddress; - - @Column(name = "user_agent", columnDefinition = "TEXT") - private String userAgent; - - @CreatedDate - @Column(name = "created_at", nullable = false, updatable = false) - private LocalDateTime createdAt; -} diff --git a/src/main/java/ru/soune/nocopy/service/EmailService.java b/src/main/java/ru/soune/nocopy/service/EmailService.java index 9a41952..ed18525 100644 --- a/src/main/java/ru/soune/nocopy/service/EmailService.java +++ b/src/main/java/ru/soune/nocopy/service/EmailService.java @@ -4,6 +4,7 @@ import jakarta.mail.MessagingException; import jakarta.mail.internet.MimeMessage; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.ClassPathResource; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.MimeMessageHelper; @@ -35,16 +36,17 @@ public class EmailService { helper.setFrom("NO COPY "); helper.setTo(user.getEmail()); - helper.setSubject("NO COPY - Подтверждение email адреса"); + helper.setSubject("NO COPY - Подтверждение email"); String htmlContent = loadAndProcessTemplate(user.getFullName(), code); - helper.setText(htmlContent, true); mailSender.send(message); - } catch (MessagingException | IOException e) { - log.error("Fail send email: " + e.getMessage()); + log.info("Email sent to: {}", user.getEmail()); + + } catch (Exception e) { + log.error("Failed to send email: {}", e.getMessage()); } } diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 493b9ce..985d913 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -1,15 +1,13 @@ spring: mail: - host: smtp.gmail.com - port: 587 - username: vlad.popovtsev@gmail.com - password: aylgtjcxdtjvohrk + host: postfix + port: 25 + username: noreply@nocopy.com + password: nocopy!nocopy! properties: - mail: - smtp: - auth: true - starttls: - enable: true + mail.smtp.auth: false + mail.smtp.starttls.enable: false + mail.debug: true datasource: url: jdbc:postgresql://${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB} @@ -36,8 +34,6 @@ spring: file: storage: base-path: ${FILE_STORAGE_PATH:/data/uploads} - # chunk-size: ${FILE_CHUNK_SIZE:5242880} # 5MB -# chunk-size: ${FILE_CHUNK_SIZE:1048576} # 1MB chunk-size: ${FILE_CHUNK_SIZE:1000000} # 1MB max-file-size: ${MAX_FILE_SIZE:10737418240} # 10GB max-retry-attempts: ${MAX_RETRY_ATTEMPTS:3} @@ -69,12 +65,3 @@ yandex: api-key: ${YANDEX_API_KEY:AQVNyaVaUmgUb1GMCtf5zSEqFxy0woXrcMOOB43q} folder-id: ${YANDEX_FOLDER_ID:b1gokpdbm6qfpsou8pcd} search-url: ${YANDEX_SEARCH_URL:https://searchapi.api.cloud.yandex.net/v2/image/search_by_image} - -app: - email: - verification: - expiration-hours: ${EMAIL_VERIFICATION_EXPIRATION:24} - base-url: ${EMAIL_BASE_URL:http://localhost:80} - from-email: ${EMAIL_FROM:noreply@yourdomain.com} - templates: - verification: verification-email.html \ No newline at end of file