mirror of
https://github.com/imgfloat/server.git
synced 2026-02-04 19:29:26 +00:00
Separate audit log db
This commit is contained in:
2
Makefile
2
Makefile
@@ -4,6 +4,7 @@
|
||||
.DEFAULT_GOAL := build
|
||||
|
||||
IMGFLOAT_DB_PATH ?= ./imgfloat.db
|
||||
IMGFLOAT_AUDIT_DB_PATH ?= ./imgfloat.audit.db
|
||||
IMGFLOAT_GITHUB_CLIENT_OWNER ?= imgfloat
|
||||
IMGFLOAT_GITHUB_CLIENT_REPO ?= client
|
||||
IMGFLOAT_GITHUB_CLIENT_VERSION ?= 1.0.0
|
||||
@@ -25,6 +26,7 @@ RUNTIME_ENV = IMGFLOAT_ASSETS_PATH=$(IMGFLOAT_ASSETS_PATH) \
|
||||
IMGFLOAT_GITHUB_CLIENT_VERSION=$(IMGFLOAT_GITHUB_CLIENT_VERSION) \
|
||||
IMGFLOAT_COMMIT_URL_PREFIX=$(IMGFLOAT_COMMIT_URL_PREFIX) \
|
||||
IMGFLOAT_DB_PATH=$(IMGFLOAT_DB_PATH) \
|
||||
IMGFLOAT_AUDIT_DB_PATH=$(IMGFLOAT_AUDIT_DB_PATH) \
|
||||
SPRING_SERVLET_MULTIPART_MAX_FILE_SIZE=$(SPRING_SERVLET_MULTIPART_MAX_FILE_SIZE) \
|
||||
SPRING_SERVLET_MULTIPART_MAX_REQUEST_SIZE=$(SPRING_SERVLET_MULTIPART_MAX_REQUEST_SIZE) \
|
||||
IMGFLOAT_TOKEN_ENCRYPTION_KEY=$(IMGFLOAT_TOKEN_ENCRYPTION_KEY)
|
||||
|
||||
@@ -13,6 +13,7 @@ Define the following required environment variables:
|
||||
| `IMGFLOAT_ASSETS_PATH` | Filesystem path to store uploaded assets | /var/imgfloat/assets |
|
||||
| `IMGFLOAT_PREVIEWS_PATH` | Filesystem path to store generated image previews | /var/imgfloat/previews |
|
||||
| `IMGFLOAT_DB_PATH` | Filesystem path to the SQLite database file | /var/imgfloat/imgfloat.db |
|
||||
| `IMGFLOAT_AUDIT_DB_PATH` | Filesystem path to the SQLite audit log database file | /var/imgfloat/imgfloat-audit.db |
|
||||
| `IMGFLOAT_INITIAL_TWITCH_USERNAME_SYSADMIN` | Twitch username of the initial sysadmin user | example_broadcaster |
|
||||
| `IMGFLOAT_GITHUB_CLIENT_OWNER` | GitHub user or org which has the client repository | imgfloat |
|
||||
| `IMGFLOAT_GITHUB_CLIENT_REPO` | Client repository name | client |
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package dev.kruhlmann.imgfloat.model;
|
||||
package dev.kruhlmann.imgfloat.audit.model;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
@@ -20,7 +20,7 @@ spring:
|
||||
thymeleaf:
|
||||
cache: false
|
||||
datasource:
|
||||
url: jdbc:sqlite:${IMGFLOAT_DB_PATH}?busy_timeout=5000&journal_mode=WAL
|
||||
url: jdbc:sqlite:${IMGFLOAT_DB_PATH:./imgfloat.db}?busy_timeout=5000&journal_mode=WAL
|
||||
driver-class-name: org.sqlite.JDBC
|
||||
hikari:
|
||||
connection-init-sql: "PRAGMA journal_mode=WAL; PRAGMA busy_timeout=5000;"
|
||||
@@ -59,6 +59,20 @@ spring:
|
||||
user-info-uri: https://api.twitch.tv/helix/users
|
||||
user-name-attribute: login
|
||||
|
||||
imgfloat:
|
||||
audit:
|
||||
datasource:
|
||||
url: jdbc:sqlite:${IMGFLOAT_AUDIT_DB_PATH:./imgfloat.audit.db}?busy_timeout=5000&journal_mode=WAL
|
||||
driver-class-name: org.sqlite.JDBC
|
||||
hikari:
|
||||
connection-init-sql: "PRAGMA journal_mode=WAL; PRAGMA busy_timeout=5000;"
|
||||
maximum-pool-size: 1
|
||||
minimum-idle: 1
|
||||
flyway:
|
||||
locations: classpath:db/audit
|
||||
baseline-on-migrate: true
|
||||
baseline-version: 1
|
||||
|
||||
management:
|
||||
endpoints:
|
||||
web:
|
||||
|
||||
11
src/main/resources/db/audit/V1__channel_audit_log.sql
Normal file
11
src/main/resources/db/audit/V1__channel_audit_log.sql
Normal file
@@ -0,0 +1,11 @@
|
||||
CREATE TABLE IF NOT EXISTS channel_audit_log (
|
||||
id TEXT PRIMARY KEY,
|
||||
broadcaster TEXT NOT NULL,
|
||||
actor TEXT,
|
||||
action TEXT NOT NULL,
|
||||
details TEXT,
|
||||
created_at TIMESTAMP NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS channel_audit_log_broadcaster_idx ON channel_audit_log (broadcaster);
|
||||
CREATE INDEX IF NOT EXISTS channel_audit_log_created_at_idx ON channel_audit_log (created_at);
|
||||
Reference in New Issue
Block a user