diff --git a/build.gradle b/build.gradle index 7717599..a309cb8 100644 --- a/build.gradle +++ b/build.gradle @@ -27,17 +27,12 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' - implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-data-redis' implementation 'org.liquibase:liquibase-core' implementation 'org.springframework.boot:spring-boot-starter-websocket' implementation 'org.mapstruct:mapstruct:1.5.5.Final' - implementation 'io.jsonwebtoken:jjwt-api:0.11.5' - runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5' - runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5' - compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.5.Final' diff --git a/src/main/java/com/krylov/refound/ReFoundApplication.java b/src/main/java/com/krylov/refound/ReFoundApplication.java index df5dc19..fc3d524 100644 --- a/src/main/java/com/krylov/refound/ReFoundApplication.java +++ b/src/main/java/com/krylov/refound/ReFoundApplication.java @@ -3,7 +3,7 @@ package com.krylov.refound; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -@SpringBootApplication +@SpringBootApplication(scanBasePackages = "com.krylov.refound") public class ReFoundApplication { public static void main(String[] args) { diff --git a/src/main/java/com/krylov/refound/config/SecurityConfig.java b/src/main/java/com/krylov/refound/config/SecurityConfig.java deleted file mode 100644 index b2e4edd..0000000 --- a/src/main/java/com/krylov/refound/config/SecurityConfig.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.krylov.refound.config; - -import com.krylov.refound.service.security.JwtFilter; -import lombok.RequiredArgsConstructor; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.web.SecurityFilterChain; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; - -@Configuration -@EnableWebSecurity -@RequiredArgsConstructor -public class SecurityConfig { - private final JwtFilter jwtFilter; - - @Bean - public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { - - http - .csrf(AbstractHttpConfigurer::disable) - .authorizeHttpRequests(auth -> auth - .requestMatchers("/api/auth/**").permitAll() - .anyRequest().authenticated() - ) - .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class); - - return http.build(); - } - - @Bean - public PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); - } -} diff --git a/src/main/java/com/krylov/refound/config/WebConfig.java b/src/main/java/com/krylov/refound/config/WebConfig.java index 079ecc1..88eb95c 100644 --- a/src/main/java/com/krylov/refound/config/WebConfig.java +++ b/src/main/java/com/krylov/refound/config/WebConfig.java @@ -2,6 +2,7 @@ package com.krylov.refound.config; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @@ -16,4 +17,13 @@ public class WebConfig implements WebMvcConfigurer { .addResourceHandler("/files/**") .addResourceLocations("file:" + uploadDir); } + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**") + .allowedOriginPatterns("*") // 🔥 важно + .allowedMethods("*") + .allowedHeaders("*") + .allowCredentials(true); + } } diff --git a/src/main/java/com/krylov/refound/config/WebSocketConfig.java b/src/main/java/com/krylov/refound/config/WebSocketConfig.java index 295c945..a96d764 100644 --- a/src/main/java/com/krylov/refound/config/WebSocketConfig.java +++ b/src/main/java/com/krylov/refound/config/WebSocketConfig.java @@ -1,6 +1,5 @@ package com.krylov.refound.config; -import com.krylov.refound.util.JwtHandshakeInterceptor; import org.springframework.context.annotation.Configuration; import org.springframework.messaging.simp.config.MessageBrokerRegistry; import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; @@ -21,7 +20,6 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/ws") .setAllowedOriginPatterns("*") - .addInterceptors(new JwtHandshakeInterceptor()) // 🔥 JWT .withSockJS(); } } diff --git a/src/main/java/com/krylov/refound/controller/AuthController.java b/src/main/java/com/krylov/refound/controller/AuthController.java deleted file mode 100644 index aff50d0..0000000 --- a/src/main/java/com/krylov/refound/controller/AuthController.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.krylov.refound.controller; - -import com.krylov.refound.dto.AuthRequest; -import com.krylov.refound.dto.AuthResponse; -import com.krylov.refound.service.security.AuthService; -import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping("/api/auth") -@RequiredArgsConstructor -public class AuthController { - private final AuthService service; - - @PostMapping("/register") - public AuthResponse register(@RequestBody AuthRequest request) { - return service.register(request); - } - - @PostMapping("/login") - public AuthResponse login(@RequestBody AuthRequest request) { - return service.login(request); - } -} diff --git a/src/main/java/com/krylov/refound/repository/MessageRepository.java b/src/main/java/com/krylov/refound/repository/MessageRepository.java index fc150e8..51854d0 100644 --- a/src/main/java/com/krylov/refound/repository/MessageRepository.java +++ b/src/main/java/com/krylov/refound/repository/MessageRepository.java @@ -53,29 +53,4 @@ public interface MessageRepository extends JpaRepository { """) void markAsRead(Long userId, Long otherUserId); - @Query(""" - SELECT new com.krylov.refound.dto.DialogResponse( - CAST( - CASE - WHEN m.sender.id = :userId THEN m.receiver.id - ELSE m.sender.id - END AS long - ), - CASE - WHEN m.sender.id = :userId THEN m.receiver.email - ELSE m.sender.email - END, - m.content, - m.createdAt, - SUM(CASE WHEN m.receiver.id = :userId AND m.isRead = false THEN 1 ELSE 0 END) - ) - FROM Message m - WHERE m.sender.id = :userId OR m.receiver.id = :userId - GROUP BY - CASE WHEN m.sender.id = :userId THEN m.receiver.id ELSE m.sender.id END, - CASE WHEN m.sender.id = :userId THEN m.receiver.email ELSE m.sender.email END, - m.content, - m.createdAt - """) - List findDialogsWithUnread(Long userId); } diff --git a/src/main/java/com/krylov/refound/service/MessageService.java b/src/main/java/com/krylov/refound/service/MessageService.java index 372ea52..ef80768 100644 --- a/src/main/java/com/krylov/refound/service/MessageService.java +++ b/src/main/java/com/krylov/refound/service/MessageService.java @@ -8,6 +8,8 @@ import com.krylov.refound.exception.ApiException; import com.krylov.refound.repository.MessageRepository; import com.krylov.refound.repository.UserRepository; import java.time.LocalDateTime; +import java.util.Collection; +import java.util.Collections; import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; @@ -17,7 +19,7 @@ import org.springframework.transaction.annotation.Transactional; @Service @RequiredArgsConstructor public class MessageService { - private final MessageRepository repository; + private final MessageRepository messageRepository; private final UserRepository userRepository; private final UserService userService; @@ -35,17 +37,40 @@ public class MessageService { message.setCreatedAt(LocalDateTime.now()); message.setRead(false); - return repository.save(message); + return messageRepository.save(message); } public List getChat(Long userId) { User current = userService.getCurrentUser(); - return repository.findChat(current.getId(), userId); + return messageRepository.findChat(current.getId(), userId); } public List getDialogs() { - User current = userService.getCurrentUser(); - return repository.findDialogsWithUnread(current.getId()); + Long userId = 1L; + List dialogs = messageRepository.findDialogs(userId); + + return dialogs.stream().map(m -> { + + boolean isMeSender = m.getSender().getId().equals(userId); + + Long otherUserId = isMeSender + ? m.getReceiver().getId() + : m.getSender().getId(); + + String email = isMeSender + ? m.getReceiver().getEmail() + : m.getSender().getEmail(); + + int unread = messageRepository.countUnread(userId, otherUserId); + + return new DialogResponse( + otherUserId, + email, + m.getContent(), + m.getCreatedAt(), + (long )unread + ); + }).toList(); } @Transactional @@ -53,6 +78,6 @@ public class MessageService { User current = userService.getCurrentUser(); - repository.markAsRead(current.getId(), otherUserId); + messageRepository.markAsRead(current.getId(), otherUserId); } } diff --git a/src/main/java/com/krylov/refound/service/UserService.java b/src/main/java/com/krylov/refound/service/UserService.java index 817fe61..3ca10f4 100644 --- a/src/main/java/com/krylov/refound/service/UserService.java +++ b/src/main/java/com/krylov/refound/service/UserService.java @@ -4,7 +4,6 @@ import com.krylov.refound.entity.User; import com.krylov.refound.enums.ErrorCode; import com.krylov.refound.exception.ApiException; import com.krylov.refound.repository.UserRepository; -import com.krylov.refound.util.SecurityUtils; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; @@ -15,9 +14,8 @@ public class UserService { private final UserRepository repository; public User getCurrentUser() { - String email = SecurityUtils.getCurrentUserEmail(); - return repository.findByEmail(email) + return repository.findByEmail("moscow_city231@rambler.ru") .orElseThrow(() -> new ApiException( ErrorCode.NOT_FOUND, "User not found", diff --git a/src/main/java/com/krylov/refound/service/security/AuthService.java b/src/main/java/com/krylov/refound/service/security/AuthService.java deleted file mode 100644 index 08133ae..0000000 --- a/src/main/java/com/krylov/refound/service/security/AuthService.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.krylov.refound.service.security; - -import com.krylov.refound.dto.AuthRequest; -import com.krylov.refound.dto.AuthResponse; -import com.krylov.refound.entity.User; -import com.krylov.refound.enums.ErrorCode; -import com.krylov.refound.exception.ApiException; -import com.krylov.refound.repository.UserRepository; -import java.time.LocalDateTime; -import lombok.RequiredArgsConstructor; -import org.springframework.http.HttpStatus; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -public class AuthService { - private final UserRepository repository; - private final PasswordEncoder passwordEncoder; - private final JwtService jwtService; - - public AuthResponse register(AuthRequest request) { - - if (repository.findByEmail(request.getEmail()).isPresent()) { - throw new ApiException(ErrorCode.BAD_REQUEST, "Email already exists", HttpStatus.BAD_REQUEST); - } - - User user = new User(); - user.setEmail(request.getEmail()); - user.setPassword(passwordEncoder.encode(request.getPassword())); - user.setCreatedAt(LocalDateTime.now()); - - repository.save(user); - - String token = jwtService.generateToken(user.getEmail()); - return new AuthResponse(token); - } - - public AuthResponse login(AuthRequest request) { - - User user = repository.findByEmail(request.getEmail()) - .orElseThrow(() -> new ApiException(ErrorCode.NOT_FOUND, "User not found", HttpStatus.NOT_FOUND)); - - if (!passwordEncoder.matches(request.getPassword(), user.getPassword())) { - throw new ApiException(ErrorCode.BAD_REQUEST, "Invalid password", HttpStatus.BAD_REQUEST); - } - - String token = jwtService.generateToken(user.getEmail()); - return new AuthResponse(token); - } -} diff --git a/src/main/java/com/krylov/refound/service/security/CustomUserDetailsService.java b/src/main/java/com/krylov/refound/service/security/CustomUserDetailsService.java deleted file mode 100644 index 6e46414..0000000 --- a/src/main/java/com/krylov/refound/service/security/CustomUserDetailsService.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.krylov.refound.service.security; - -import com.krylov.refound.entity.User; -import com.krylov.refound.repository.UserRepository; -import lombok.RequiredArgsConstructor; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -public class CustomUserDetailsService implements UserDetailsService { - private final UserRepository repository; - - @Override - public UserDetails loadUserByUsername(String email) { - User user = repository.findByEmail(email) - .orElseThrow(() -> new UsernameNotFoundException("User not found")); - - return org.springframework.security.core.userdetails.User - .withUsername(user.getEmail()) - .password(user.getPassword()) - .authorities("USER") - .build(); - } -} diff --git a/src/main/java/com/krylov/refound/service/security/JwtFilter.java b/src/main/java/com/krylov/refound/service/security/JwtFilter.java deleted file mode 100644 index a27376a..0000000 --- a/src/main/java/com/krylov/refound/service/security/JwtFilter.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.krylov.refound.service.security; - -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import java.io.IOException; -import lombok.RequiredArgsConstructor; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.stereotype.Component; -import org.springframework.web.filter.OncePerRequestFilter; - -@Component -@RequiredArgsConstructor -public class JwtFilter extends OncePerRequestFilter { - - private final JwtService jwtService; - private final CustomUserDetailsService userDetailsService; - - @Override - protected void doFilterInternal( - HttpServletRequest request, - HttpServletResponse response, - FilterChain filterChain - ) throws ServletException, IOException { - - String header = request.getHeader("Authorization"); - - if (header == null || !header.startsWith("Bearer ")) { - filterChain.doFilter(request, response); - return; - } - - String token = header.substring(7); - String email = jwtService.extractEmail(token); - - if (email != null && SecurityContextHolder.getContext().getAuthentication() == null) { - - UserDetails userDetails = userDetailsService.loadUserByUsername(email); - - UsernamePasswordAuthenticationToken auth = - new UsernamePasswordAuthenticationToken( - userDetails, - null, - userDetails.getAuthorities() - ); - - SecurityContextHolder.getContext().setAuthentication(auth); - } - - filterChain.doFilter(request, response); - } -} diff --git a/src/main/java/com/krylov/refound/service/security/JwtService.java b/src/main/java/com/krylov/refound/service/security/JwtService.java deleted file mode 100644 index 027bb8f..0000000 --- a/src/main/java/com/krylov/refound/service/security/JwtService.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.krylov.refound.service.security; - -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.security.Keys; -import java.util.Date; -import org.springframework.stereotype.Service; - -@Service -public class JwtService { - private final String SECRET = "very_secret_key_123"; - - public String generateToken(String email) { - return Jwts.builder() - .setSubject(email) - .setIssuedAt(new Date()) - .setExpiration(new Date(System.currentTimeMillis() + 86400000)) // 1 день - .signWith(Keys.hmacShaKeyFor(SECRET.getBytes())) - .compact(); - } - - public String extractEmail(String token) { - return Jwts.parserBuilder() - .setSigningKey(SECRET.getBytes()) - .build() - .parseClaimsJws(token) - .getBody() - .getSubject(); - } -} diff --git a/src/main/java/com/krylov/refound/util/JwtHandshakeInterceptor.java b/src/main/java/com/krylov/refound/util/JwtHandshakeInterceptor.java deleted file mode 100644 index a0aba15..0000000 --- a/src/main/java/com/krylov/refound/util/JwtHandshakeInterceptor.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.krylov.refound.util; - -import java.util.List; -import java.util.Map; -import org.springframework.http.server.ServerHttpRequest; -import org.springframework.http.server.ServerHttpResponse; -import org.springframework.web.socket.WebSocketHandler; -import org.springframework.web.socket.server.HandshakeInterceptor; - -public class JwtHandshakeInterceptor implements HandshakeInterceptor { - - @Override - public boolean beforeHandshake( - ServerHttpRequest request, - ServerHttpResponse response, - WebSocketHandler wsHandler, - Map attributes - ) throws Exception { - - List auth = request.getHeaders().get("Authorization"); - - if (auth == null || auth.isEmpty()) { - return false; // запретить подключение - } - - String token = auth.get(0).replace("Bearer ", ""); - - // 🔥 проверяем JWT - String userEmail = JwtUtils.validateTokenAndGetEmail(token); - - if (userEmail == null) { - return false; - } - - attributes.put("userEmail", userEmail); - return true; - } - - @Override - public void afterHandshake( - ServerHttpRequest request, - ServerHttpResponse response, - WebSocketHandler wsHandler, - Exception exception - ) { } -} \ No newline at end of file diff --git a/src/main/java/com/krylov/refound/util/JwtUtils.java b/src/main/java/com/krylov/refound/util/JwtUtils.java deleted file mode 100644 index ab42a8d..0000000 --- a/src/main/java/com/krylov/refound/util/JwtUtils.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.krylov.refound.util; - -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.security.Keys; -import org.springframework.stereotype.Component; - -@Component -public class JwtUtils { - private static final String SECRET = "SECRET_KEY"; // заменить на env var - - public static String validateTokenAndGetEmail(String token) { - try { - return Jwts.parserBuilder() - .setSigningKey(Keys.hmacShaKeyFor(SECRET.getBytes())) - .build() - .parseClaimsJws(token) - .getBody() - .getSubject(); - } catch (Exception e) { - return null; - } - } -} diff --git a/src/main/java/com/krylov/refound/util/SecurityUtils.java b/src/main/java/com/krylov/refound/util/SecurityUtils.java deleted file mode 100644 index 0eb9e13..0000000 --- a/src/main/java/com/krylov/refound/util/SecurityUtils.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.krylov.refound.util; - -import org.springframework.security.core.context.SecurityContextHolder; - -public class SecurityUtils { - public static String getCurrentUserEmail() { - return SecurityContextHolder.getContext() - .getAuthentication() - .getName(); - } -} diff --git a/src/main/resources/db/changelog/001-init.yaml b/src/main/resources/db/changelog/001-init.yaml deleted file mode 100644 index 904d0b1..0000000 --- a/src/main/resources/db/changelog/001-init.yaml +++ /dev/null @@ -1,57 +0,0 @@ -databaseChangeLog: - - changeSet: - id: 001-create-users - author: you - changes: - - createTable: - tableName: users - columns: - - column: { name: id, type: BIGSERIAL, constraints: { primaryKey: true } } - - column: { name: email, type: VARCHAR(255), constraints: { nullable: false, unique: true } } - - column: { name: password, type: VARCHAR(255), constraints: { nullable: false } } - - column: { name: created_at, type: TIMESTAMP } - - - changeSet: - id: 002-create-posts - author: you - changes: - - createTable: - tableName: posts - columns: - - column: { name: id, type: BIGSERIAL, constraints: { primaryKey: true } } - - column: { name: type, type: VARCHAR(20) } - - column: { name: title, type: VARCHAR(255) } - - column: { name: description, type: TEXT } - - column: { name: category, type: VARCHAR(100) } - - column: { name: city, type: VARCHAR(100) } - - column: { name: district, type: VARCHAR(100) } - - column: { name: latitude, type: DOUBLE } - - column: { name: longitude, type: DOUBLE } - - column: { name: status, type: VARCHAR(20) } - - column: { name: created_at, type: TIMESTAMP } - - column: { name: user_id, type: BIGINT } - - - addForeignKeyConstraint: - baseTableName: posts - baseColumnNames: user_id - referencedTableName: users - referencedColumnNames: id - constraintName: fk_posts_user # ✅ ДОБАВИТЬ - - - changeSet: - id: 003-create-images - author: you - changes: - - createTable: - tableName: images - columns: - - column: { name: id, type: BIGSERIAL, constraints: { primaryKey: true } } - - column: { name: url, type: VARCHAR(500) } - - column: { name: post_id, type: BIGINT } - - - addForeignKeyConstraint: - baseTableName: images - baseColumnNames: post_id - referencedTableName: posts - referencedColumnNames: id - constraintName: fk_images_post # ✅ \ No newline at end of file diff --git a/src/main/resources/db/changelog/constraint/006-fk-images-post-constraint.yaml b/src/main/resources/db/changelog/constraint/006-fk-images-post-constraint.yaml new file mode 100644 index 0000000..1605edb --- /dev/null +++ b/src/main/resources/db/changelog/constraint/006-fk-images-post-constraint.yaml @@ -0,0 +1,11 @@ +databaseChangeLog: + - changeSet: + id: 007-constraint-images + author: alexandr krylov + changes: + - addForeignKeyConstraint: + baseTableName: images + baseColumnNames: post_id + referencedTableName: posts + referencedColumnNames: id + constraintName: fk_images_post \ No newline at end of file diff --git a/src/main/resources/db/changelog/constraint/007-fk-post-user-constraint.yaml b/src/main/resources/db/changelog/constraint/007-fk-post-user-constraint.yaml new file mode 100644 index 0000000..7405044 --- /dev/null +++ b/src/main/resources/db/changelog/constraint/007-fk-post-user-constraint.yaml @@ -0,0 +1,11 @@ +databaseChangeLog: + - changeSet: + id: 006-constraint-post + author: alexandr krylov + changes: + - addForeignKeyConstraint: + baseTableName: posts + baseColumnNames: user_id + referencedTableName: users + referencedColumnNames: id + constraintName: fk_posts_user \ No newline at end of file diff --git a/src/main/resources/db/changelog/002-message.yaml b/src/main/resources/db/changelog/constraint/008-fk-message-user.yaml similarity index 54% rename from src/main/resources/db/changelog/002-message.yaml rename to src/main/resources/db/changelog/constraint/008-fk-message-user.yaml index 9a5f17c..c50c4e2 100644 --- a/src/main/resources/db/changelog/002-message.yaml +++ b/src/main/resources/db/changelog/constraint/008-fk-message-user.yaml @@ -1,31 +1,21 @@ databaseChangeLog: - changeSet: - id: 004-create-messages - author: you + id: 008-constraint-message + author: alexandr krylov changes: - - createTable: - tableName: messages - columns: - - column: { name: id, type: BIGSERIAL, constraints: { primaryKey: true } } - - column: { name: sender_id, type: BIGINT } - - column: { name: receiver_id, type: BIGINT } - - column: { name: content, type: TEXT } - - column: { name: created_at, type: TIMESTAMP } - - column: { name: is_read, type: BOOLEAN } - - addForeignKeyConstraint: baseTableName: messages baseColumnNames: sender_id referencedTableName: users referencedColumnNames: id - constraintName: fk_messages_sender # ✅ + constraintName: fk_messages_sender - addForeignKeyConstraint: baseTableName: messages baseColumnNames: receiver_id referencedTableName: users referencedColumnNames: id - constraintName: fk_messages_receiver # ✅ + constraintName: fk_messages_receiver # 🔥 индекс для чата - createIndex: diff --git a/src/main/resources/db/changelog/003-favorite.yaml b/src/main/resources/db/changelog/constraint/009-fk-favorite-user.yaml similarity index 58% rename from src/main/resources/db/changelog/003-favorite.yaml rename to src/main/resources/db/changelog/constraint/009-fk-favorite-user.yaml index c9a1ea5..3efad1d 100644 --- a/src/main/resources/db/changelog/003-favorite.yaml +++ b/src/main/resources/db/changelog/constraint/009-fk-favorite-user.yaml @@ -1,28 +1,21 @@ databaseChangeLog: - changeSet: - id: 005-create-favorites - author: you + id: 009-fk-favorite-user + author: alexandr krylov changes: - - createTable: - tableName: favorites - columns: - - column: { name: id, type: BIGSERIAL, constraints: { primaryKey: true } } - - column: { name: user_id, type: BIGINT } - - column: { name: post_id, type: BIGINT } - - addForeignKeyConstraint: baseTableName: favorites baseColumnNames: user_id referencedTableName: users referencedColumnNames: id - constraintName: fk_favorites_user # ✅ + constraintName: fk_favorites_user - addForeignKeyConstraint: baseTableName: favorites baseColumnNames: post_id referencedTableName: posts referencedColumnNames: id - constraintName: fk_favorites_post # ✅ + constraintName: fk_favorites_post # 🔥 защита от дублей - addUniqueConstraint: diff --git a/src/main/resources/db/changelog/create/001-create-users.yaml b/src/main/resources/db/changelog/create/001-create-users.yaml new file mode 100644 index 0000000..1c97d9d --- /dev/null +++ b/src/main/resources/db/changelog/create/001-create-users.yaml @@ -0,0 +1,12 @@ +databaseChangeLog: + - changeSet: + id: 001-create-users + author: you + changes: + - createTable: + tableName: users + columns: + - column: { name: id, type: BIGSERIAL, constraints: { primaryKey: true } } + - column: { name: email, type: VARCHAR(255), constraints: { nullable: false, unique: true } } + - column: { name: password, type: VARCHAR(255), constraints: { nullable: false } } + - column: { name: created_at, type: TIMESTAMP } \ No newline at end of file diff --git a/src/main/resources/db/changelog/create/002-create-posts.yaml b/src/main/resources/db/changelog/create/002-create-posts.yaml new file mode 100644 index 0000000..2d73bb6 --- /dev/null +++ b/src/main/resources/db/changelog/create/002-create-posts.yaml @@ -0,0 +1,20 @@ +databaseChangeLog: + - changeSet: + id: 005-create-posts + author: you + changes: + - createTable: + tableName: posts + columns: + - column: { name: id, type: BIGSERIAL, constraints: { primaryKey: true } } + - column: { name: type, type: VARCHAR(20) } + - column: { name: title, type: VARCHAR(255) } + - column: { name: description, type: TEXT } + - column: { name: category, type: VARCHAR(100) } + - column: { name: city, type: VARCHAR(100) } + - column: { name: district, type: VARCHAR(100) } + - column: { name: latitude, type: DOUBLE } + - column: { name: longitude, type: DOUBLE } + - column: { name: status, type: VARCHAR(20) } + - column: { name: created_at, type: TIMESTAMP } + - column: { name: user_id, type: BIGINT } \ No newline at end of file diff --git a/src/main/resources/db/changelog/create/003-create-images.yaml b/src/main/resources/db/changelog/create/003-create-images.yaml new file mode 100644 index 0000000..35135b8 --- /dev/null +++ b/src/main/resources/db/changelog/create/003-create-images.yaml @@ -0,0 +1,11 @@ +databaseChangeLog: + - changeSet: + id: 003-create-images + author: you + changes: + - createTable: + tableName: images + columns: + - column: { name: id, type: BIGSERIAL, constraints: { primaryKey: true } } + - column: { name: url, type: VARCHAR(500) } + - column: { name: post_id, type: BIGINT } \ No newline at end of file diff --git a/src/main/resources/db/changelog/create/004-create-message.yaml b/src/main/resources/db/changelog/create/004-create-message.yaml new file mode 100644 index 0000000..253ad1f --- /dev/null +++ b/src/main/resources/db/changelog/create/004-create-message.yaml @@ -0,0 +1,14 @@ +databaseChangeLog: + - changeSet: + id: 004-create-messages + author: you + changes: + - createTable: + tableName: messages + columns: + - column: { name: id, type: BIGSERIAL, constraints: { primaryKey: true } } + - column: { name: sender_id, type: BIGINT } + - column: { name: receiver_id, type: BIGINT } + - column: { name: content, type: TEXT } + - column: { name: created_at, type: TIMESTAMP } + - column: { name: is_read, type: BOOLEAN } \ No newline at end of file diff --git a/src/main/resources/db/changelog/create/005-create-favorite.yaml b/src/main/resources/db/changelog/create/005-create-favorite.yaml new file mode 100644 index 0000000..c0de7a6 --- /dev/null +++ b/src/main/resources/db/changelog/create/005-create-favorite.yaml @@ -0,0 +1,11 @@ +databaseChangeLog: + - changeSet: + id: 005-create-favorites + author: you + changes: + - createTable: + tableName: favorites + columns: + - column: { name: id, type: BIGSERIAL, constraints: { primaryKey: true } } + - column: { name: user_id, type: BIGINT } + - column: { name: post_id, type: BIGINT } \ No newline at end of file diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml index b0b3410..73c5227 100644 --- a/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -1,9 +1,10 @@ databaseChangeLog: - - include: - file: db/changelog/001-init.yaml - - - include: - file: db/changelog/002-message.yaml - - - include: - file: db/changelog/003-favorite.yaml \ No newline at end of file + - include: { file: db/changelog/create/001-create-users.yaml } + - include: { file: db/changelog/create/002-create-posts.yaml } + - include: { file: db/changelog/create/003-create-images.yaml } + - include: { file: db/changelog/create/004-create-message.yaml } + - include: { file: db/changelog/create/005-create-favorite.yaml } + - include: { file: db/changelog/constraint/006-fk-images-post-constraint.yaml } + - include: { file: db/changelog/constraint/007-fk-post-user-constraint.yaml } + - include: { file: db/changelog/constraint/008-fk-message-user.yaml } + - include: { file: db/changelog/constraint/009-fk-favorite-user.yaml } \ No newline at end of file