feat: Implement database initialization using init.sql and update DataInitializer.
All checks were successful
Build and Push Docker Images / build-and-push-backend (push) Successful in 34s
Build and Push Docker Images / build-and-push-frontend (push) Successful in 11s
Build and Push Docker Images / deploy-to-k8s (push) Successful in 45s

This commit is contained in:
Zuev
2026-03-13 00:21:06 +03:00
parent 6ea420e529
commit 4c2293b620
2 changed files with 285 additions and 27 deletions

View File

@@ -3,61 +3,90 @@ package com.magistr.app.config;
import com.magistr.app.config.tenant.TenantConfig;
import com.magistr.app.config.tenant.TenantContext;
import com.magistr.app.config.tenant.TenantRoutingDataSource;
import com.magistr.app.model.Role;
import com.magistr.app.model.User;
import com.magistr.app.repository.UserRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import java.util.Optional;
import javax.sql.DataSource;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.stream.Collectors;
/**
* При запуске приложения проверяет каждый тенант:
* - Если таблицы не существуют — выполняет init.sql
* - init.sql создаёт все таблицы + admin (admin/admin) + тестовые данные
*/
@Component
public class DataInitializer implements CommandLineRunner {
private static final Logger log = LoggerFactory.getLogger(DataInitializer.class);
private final UserRepository userRepository;
private final BCryptPasswordEncoder passwordEncoder;
private final TenantRoutingDataSource routingDataSource;
private final DataSource dataSource;
public DataInitializer(UserRepository userRepository,
BCryptPasswordEncoder passwordEncoder,
TenantRoutingDataSource routingDataSource) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
public DataInitializer(TenantRoutingDataSource routingDataSource, DataSource dataSource) {
this.routingDataSource = routingDataSource;
this.dataSource = dataSource;
}
@Override
public void run(String... args) {
// Создаём admin в каждом тенанте
for (TenantConfig tenant : routingDataSource.getTenantConfigs().values()) {
String domain = tenant.getDomain();
try {
TenantContext.setCurrentTenant(tenant.getDomain());
initAdmin(tenant.getDomain());
TenantContext.setCurrentTenant(domain);
if (needsInit()) {
log.info("[{}] Tables not found — executing init.sql...", domain);
executeInitSql();
log.info("[{}] init.sql executed successfully", domain);
} else {
log.info("[{}] Tables already exist, skipping init", domain);
}
} catch (Exception e) {
log.error("Failed to init admin for tenant '{}': {}", tenant.getDomain(), e.getMessage());
log.error("[{}] Initialization failed: {}", domain, e.getMessage());
} finally {
TenantContext.clear();
}
}
}
private void initAdmin(String tenantDomain) {
Optional<User> existing = userRepository.findByUsername("admin");
/**
* Проверяет, существует ли таблица 'users' в текущей БД тенанта.
*/
private boolean needsInit() {
try (Connection conn = dataSource.getConnection();
ResultSet rs = conn.getMetaData().getTables(null, null, "users", new String[]{"TABLE"})) {
return !rs.next();
} catch (Exception e) {
log.warn("Could not check tables: {}", e.getMessage());
return true; // Если не смогли проверить — пробуем init
}
}
if (existing.isEmpty()) {
User admin = new User();
admin.setUsername("admin");
admin.setPassword(passwordEncoder.encode("admin"));
admin.setRole(Role.ADMIN);
userRepository.save(admin);
log.info("[{}] Created default admin user (admin/admin)", tenantDomain);
} else {
log.info("[{}] Admin user already exists", tenantDomain);
/**
* Читает init.sql из classpath и выполняет его через JDBC.
*/
private void executeInitSql() throws Exception {
// Читаем SQL файл из ресурсов
String sql;
try (InputStream is = new ClassPathResource("init.sql").getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
sql = reader.lines().collect(Collectors.joining("\n"));
}
// Выполняем SQL
try (Connection conn = dataSource.getConnection();
Statement stmt = conn.createStatement()) {
stmt.execute(sql);
}
}
}