feat(db): add PostgreSQL Spring profile

This commit is contained in:
matevip 2026-06-14 16:47:02 +08:00
parent d7418e49df
commit 07da1d610b
2 changed files with 91 additions and 1 deletions

View File

@ -44,6 +44,9 @@ public class DatabaseBootstrapRunner implements ApplicationRunner {
/** Cached flag: true when running on KingbaseES. */
private volatile Boolean isKingbase;
/** Cached flag: true when running on PostgreSQL. */
private volatile Boolean isPostgres;
/**
* When true, wait for Desktop splash screen to call /setup/init with chosen language.
* When false (default), auto-initialize immediately on startup.
@ -114,7 +117,9 @@ public class DatabaseBootstrapRunner implements ApplicationRunner {
String scriptName;
if (isMySQL()) {
scriptName = "en-US".equals(locale) ? "db/data-mysql-en.sql" : "db/data-mysql-zh.sql";
} else if (isKingbase()) {
} else if (isKingbase() || isPostgres()) {
// PostgreSQL-family seed (covers both PostgreSQL and KingbaseES,
// which share the same ON CONFLICT / SERIAL-free DDL dialect).
scriptName = "en-US".equals(locale) ? "db/data-kingbase-en.sql" : "db/data-kingbase-zh.sql";
} else {
scriptName = "en-US".equals(locale) ? "db/data-en.sql" : "db/data-zh.sql";
@ -165,8 +170,13 @@ public class DatabaseBootstrapRunner implements ApplicationRunner {
String dbProduct = connection.getMetaData().getDatabaseProductName().toLowerCase();
isMySQL = dbProduct.contains("mysql") || dbProduct.contains("mariadb");
isKingbase = dbProduct.contains("kingbase");
// KingbaseES reports its own product name ("KingbaseES"), so the
// postgres check stays mutually exclusive with the kingbase one.
isPostgres = dbProduct.contains("postgresql") && !isKingbase;
if (isKingbase) {
log.info("Detected database: {} (KingbaseES mode)", dbProduct);
} else if (isPostgres) {
log.info("Detected database: {} (PostgreSQL mode)", dbProduct);
} else {
log.info("Detected database: {} (MySQL mode: {})", dbProduct, isMySQL);
}
@ -174,6 +184,7 @@ public class DatabaseBootstrapRunner implements ApplicationRunner {
log.warn("Failed to detect database type, falling back to H2 mode", e);
isMySQL = false;
isKingbase = false;
isPostgres = false;
}
}
return isMySQL;
@ -187,6 +198,14 @@ public class DatabaseBootstrapRunner implements ApplicationRunner {
return isKingbase != null && isKingbase;
}
private boolean isPostgres() {
if (isPostgres == null) {
// Trigger detection
isMySQL();
}
return isPostgres != null && isPostgres;
}
private void runScript(String path) {
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
populator.setContinueOnError(false);

View File

@ -0,0 +1,71 @@
spring:
datasource:
# PostgreSQL data source.
# Default connection parameters:
# DB_HOST=localhost, DB_PORT=5432, DB_NAME=mateclaw
# Override via env vars: DB_HOST, DB_PORT, DB_NAME, DB_USERNAME, DB_PASSWORD
#
# JDBC timeouts:
# connectTimeout=10 — TCP connect timeout (s), avoids OS-level stalls
# socketTimeout=30 — socket read timeout (s), prevents dead connections hanging forever
# loginTimeout=10 — database login timeout (s)
url: jdbc:postgresql://${DB_HOST:localhost}:${DB_PORT:5432}/${DB_NAME:mateclaw}?currentSchema=mateclaw&connectTimeout=10&socketTimeout=30&loginTimeout=10
driver-class-name: org.postgresql.Driver
username: ${DB_USERNAME:postgres}
password: ${DB_PASSWORD:postgres}
hikari:
maximum-pool-size: 30
minimum-idle: 5
connection-timeout: 30000
idle-timeout: 300000
# Recycle connections after 10 min so a leak self-heals quickly rather
# than surfacing as a ~30 min stall.
max-lifetime: 600000
leak-detection-threshold: 30000
# Fail fast if the first valid connection can't be obtained in time.
initialization-fail-timeout: 30000
# Force search_path on every new connection as a belt-and-suspenders
# guard alongside currentSchema, so connections never land in public.
connection-init-sql: SET search_path TO mateclaw
flyway:
# PostgreSQL and KingbaseES share the same migration tree
# (db/migration/kingbase) because they use the same SQL dialect.
url: jdbc:postgresql://${DB_HOST:localhost}:${DB_PORT:5432}/${DB_NAME:mateclaw}?currentSchema=mateclaw&connectTimeout=10&socketTimeout=30&loginTimeout=10
user: ${DB_USERNAME:postgres}
password: ${DB_PASSWORD:postgres}
locations:
- classpath:db/migration/kingbase
# Ensure the target schema exists before migrating (PostgreSQL won't
# auto-create a non-public schema).
init-sqls:
- CREATE SCHEMA IF NOT EXISTS mateclaw
# Skip checksum validation so an in-place migration edit doesn't block startup.
validate-on-migrate: false
h2:
console:
enabled: false
# MyBatis Plus — explicit PostgreSQL dialect.
# The no-arg PaginationInnerInterceptor auto-detects from the JDBC URL, which
# can fail under a wrapped/proxied DataSource and fall back to the MySQL
# dialect (LIMIT offset,count). Pin postgre_sql so pagination / ID generation /
# batch operations always use the correct dialect.
mybatis-plus:
global-config:
db-config:
db-type: postgre_sql
# Production (multi-tenant server) hardening: fail closed on Wiki source-path
# validation. With no allowed-source-roots configured, every KB source
# directory is rejected rather than allowing full-filesystem reads — a missing
# allow-list cannot silently re-open arbitrary directory scanning. Override
# MATE_WIKI_ALLOWED_SOURCE_ROOTS to permit specific roots. The default profile
# (H2 / desktop / single-tenant) leaves this off.
mate:
wiki:
require-allowed-roots: true
allowed-source-roots: ${MATE_WIKI_ALLOWED_SOURCE_ROOTS:}
watcher-enabled: ${MATE_WIKI_WATCHER_ENABLED:false}
watcher-interval-ms: ${MATE_WIKI_WATCHER_INTERVAL_MS:300000}