Separate audit log db

This commit is contained in:
2026-01-23 18:14:09 +01:00
parent e578007115
commit c96918340a
11 changed files with 229 additions and 6 deletions

View File

@@ -1,4 +1,4 @@
package dev.kruhlmann.imgfloat.model;
package dev.kruhlmann.imgfloat.audit.model;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;

View File

@@ -0,0 +1,90 @@
package dev.kruhlmann.imgfloat.config;
import com.zaxxer.hikari.HikariDataSource;
import jakarta.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import org.flywaydb.core.Flyway;
import org.flywaydb.core.api.configuration.FluentConfiguration;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.flyway.FlywayProperties;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateProperties;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateSettings;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
@Configuration
@EnableJpaRepositories(
basePackages = "dev.kruhlmann.imgfloat.repository.audit",
entityManagerFactoryRef = "auditEntityManagerFactory",
transactionManagerRef = "auditTransactionManager"
)
public class AuditLogDataSourceConfig {
@Bean
@ConfigurationProperties("imgfloat.audit.datasource")
public DataSourceProperties auditDataSourceProperties() {
return new DataSourceProperties();
}
@Bean
@ConfigurationProperties("imgfloat.audit.datasource.hikari")
public HikariDataSource auditDataSource(
@Qualifier("auditDataSourceProperties") DataSourceProperties properties
) {
return properties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
}
@Bean
public LocalContainerEntityManagerFactoryBean auditEntityManagerFactory(
EntityManagerFactoryBuilder builder,
@Qualifier("auditDataSource") DataSource dataSource,
JpaProperties jpaProperties,
HibernateProperties hibernateProperties
) {
return builder
.dataSource(dataSource)
.packages("dev.kruhlmann.imgfloat.audit.model")
.properties(hibernateProperties.determineHibernateProperties(jpaProperties.getProperties(), new HibernateSettings()))
.persistenceUnit("audit")
.build();
}
@Bean
public PlatformTransactionManager auditTransactionManager(
@Qualifier("auditEntityManagerFactory") EntityManagerFactory entityManagerFactory
) {
return new JpaTransactionManager(entityManagerFactory);
}
@Bean
@ConfigurationProperties("imgfloat.audit.flyway")
public FlywayProperties auditFlywayProperties() {
return new FlywayProperties();
}
@Bean(initMethod = "migrate")
public Flyway auditFlyway(
@Qualifier("auditDataSource") DataSource dataSource,
@Qualifier("auditFlywayProperties") FlywayProperties properties
) {
FluentConfiguration configuration = Flyway.configure().dataSource(dataSource);
if (properties.getLocations() != null && !properties.getLocations().isEmpty()) {
configuration.locations(properties.getLocations().toArray(new String[0]));
}
if (properties.isBaselineOnMigrate()) {
configuration.baselineOnMigrate(true);
}
if (properties.getBaselineVersion() != null) {
configuration.baselineVersion(properties.getBaselineVersion());
}
return configuration.load();
}
}

View File

@@ -0,0 +1,99 @@
package dev.kruhlmann.imgfloat.config;
import com.zaxxer.hikari.HikariDataSource;
import dev.kruhlmann.imgfloat.repository.audit.AuditLogRepository;
import jakarta.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import org.flywaydb.core.Flyway;
import org.flywaydb.core.api.configuration.FluentConfiguration;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.flyway.FlywayProperties;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateProperties;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateSettings;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
@Configuration
@EnableJpaRepositories(
basePackages = "dev.kruhlmann.imgfloat.repository",
excludeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = AuditLogRepository.class),
entityManagerFactoryRef = "entityManagerFactory",
transactionManagerRef = "transactionManager"
)
public class PrimaryDataSourceConfig {
@Bean
@Primary
@ConfigurationProperties("spring.datasource")
public DataSourceProperties dataSourceProperties() {
return new DataSourceProperties();
}
@Bean
@Primary
@ConfigurationProperties("spring.datasource.hikari")
public HikariDataSource dataSource(@Qualifier("dataSourceProperties") DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
}
@Bean
@Primary
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
EntityManagerFactoryBuilder builder,
@Qualifier("dataSource") DataSource dataSource,
JpaProperties jpaProperties,
HibernateProperties hibernateProperties
) {
return builder
.dataSource(dataSource)
.packages("dev.kruhlmann.imgfloat.model")
.properties(hibernateProperties.determineHibernateProperties(jpaProperties.getProperties(), new HibernateSettings()))
.persistenceUnit("primary")
.build();
}
@Bean
@Primary
public PlatformTransactionManager transactionManager(
@Qualifier("entityManagerFactory") EntityManagerFactory entityManagerFactory
) {
return new JpaTransactionManager(entityManagerFactory);
}
@Bean
@Primary
@ConfigurationProperties("spring.flyway")
public FlywayProperties flywayProperties() {
return new FlywayProperties();
}
@Bean(initMethod = "migrate")
@Primary
public Flyway flyway(
@Qualifier("dataSource") DataSource dataSource,
@Qualifier("flywayProperties") FlywayProperties properties
) {
FluentConfiguration configuration = Flyway.configure().dataSource(dataSource);
if (properties.getLocations() != null && !properties.getLocations().isEmpty()) {
configuration.locations(properties.getLocations().toArray(new String[0]));
}
if (properties.isBaselineOnMigrate()) {
configuration.baselineOnMigrate(true);
}
if (properties.getBaselineVersion() != null) {
configuration.baselineVersion(properties.getBaselineVersion());
}
return configuration.load();
}
}

View File

@@ -37,6 +37,9 @@ public class SystemEnvironmentValidator {
@Value("${IMGFLOAT_DB_PATH:#{null}}")
private String dbPath;
@Value("${IMGFLOAT_AUDIT_DB_PATH:${IMGFLOAT_DB_PATH:#{null}}}")
private String auditDbPath;
@Value("${IMGFLOAT_INITIAL_TWITCH_USERNAME_SYSADMIN:#{null}}")
private String initialSysadmin;
@@ -76,6 +79,7 @@ public class SystemEnvironmentValidator {
checkString(twitchClientId, "TWITCH_CLIENT_ID", missing);
checkString(initialSysadmin, "IMGFLOAT_INITIAL_TWITCH_USERNAME_SYSADMIN", missing);
checkString(dbPath, "IMGFLOAT_DB_PATH", missing);
checkString(auditDbPath, "IMGFLOAT_AUDIT_DB_PATH", missing);
checkString(twitchClientSecret, "TWITCH_CLIENT_SECRET", missing);
checkString(assetsPath, "IMGFLOAT_ASSETS_PATH", missing);
checkString(previewsPath, "IMGFLOAT_PREVIEWS_PATH", missing);
@@ -93,6 +97,7 @@ public class SystemEnvironmentValidator {
log.info(" - SPRING_SERVLET_MULTIPART_MAX_FILE_SIZE: {} ({} bytes)", springMaxFileSize, maxUploadBytes);
log.info(" - SPRING_SERVLET_MULTIPART_MAX_REQUEST_SIZE: {} ({} bytes)", springMaxRequestSize, maxRequestBytes);
log.info(" - IMGFLOAT_DB_PATH: {}", dbPath);
log.info(" - IMGFLOAT_AUDIT_DB_PATH: {}", auditDbPath);
log.info(" - IMGFLOAT_INITIAL_TWITCH_USERNAME_SYSADMIN: {}", initialSysadmin);
log.info(" - IMGFLOAT_ASSETS_PATH: {}", assetsPath);
log.info(" - IMGFLOAT_PREVIEWS_PATH: {}", previewsPath);

View File

@@ -1,5 +1,6 @@
package dev.kruhlmann.imgfloat.model;
import dev.kruhlmann.imgfloat.audit.model.AuditLogEntry;
import java.time.Instant;
public record AuditLogEntryView(String id, String actor, String action, String details, Instant createdAt) {

View File

@@ -1,6 +1,6 @@
package dev.kruhlmann.imgfloat.repository;
package dev.kruhlmann.imgfloat.repository.audit;
import dev.kruhlmann.imgfloat.model.AuditLogEntry;
import dev.kruhlmann.imgfloat.audit.model.AuditLogEntry;
import java.util.List;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

View File

@@ -1,8 +1,8 @@
package dev.kruhlmann.imgfloat.service;
import dev.kruhlmann.imgfloat.model.AuditLogEntry;
import dev.kruhlmann.imgfloat.audit.model.AuditLogEntry;
import dev.kruhlmann.imgfloat.model.AuditLogEntryView;
import dev.kruhlmann.imgfloat.repository.AuditLogRepository;
import dev.kruhlmann.imgfloat.repository.audit.AuditLogRepository;
import dev.kruhlmann.imgfloat.util.LogSanitizer;
import java.util.List;
import java.util.Locale;