added liquibase db
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -53,29 +53,4 @@ public interface MessageRepository extends JpaRepository<Message, Long> {
|
||||
""")
|
||||
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<DialogResponse> findDialogsWithUnread(Long userId);
|
||||
}
|
||||
|
||||
@@ -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<Message> getChat(Long userId) {
|
||||
User current = userService.getCurrentUser();
|
||||
return repository.findChat(current.getId(), userId);
|
||||
return messageRepository.findChat(current.getId(), userId);
|
||||
}
|
||||
|
||||
public List<DialogResponse> getDialogs() {
|
||||
User current = userService.getCurrentUser();
|
||||
return repository.findDialogsWithUnread(current.getId());
|
||||
Long userId = 1L;
|
||||
List<Message> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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<String, Object> attributes
|
||||
) throws Exception {
|
||||
|
||||
List<String> 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
|
||||
) { }
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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 # ✅
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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:
|
||||
@@ -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:
|
||||
12
src/main/resources/db/changelog/create/001-create-users.yaml
Normal file
12
src/main/resources/db/changelog/create/001-create-users.yaml
Normal file
@@ -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 }
|
||||
20
src/main/resources/db/changelog/create/002-create-posts.yaml
Normal file
20
src/main/resources/db/changelog/create/002-create-posts.yaml
Normal file
@@ -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 }
|
||||
@@ -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 }
|
||||
@@ -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 }
|
||||
@@ -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 }
|
||||
@@ -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
|
||||
- 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 }
|
||||
Reference in New Issue
Block a user