diff --git a/.gitignore b/.gitignore index ec22b8e..9faf0f9 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,7 @@ gradle/ build/ logs/ -workdir/ +data/ dokka/ dist/ diff --git a/build.gradle.kts b/build.gradle.kts index bec1dd1..46876d0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -30,7 +30,7 @@ dependencies { implementation("org.apache.pdfbox:pdfbox:2.0.4") implementation("com.atlassian.commonmark:commonmark:0.13.1") implementation("commons-io:commons-io:2.6") - implementation("dev.fyloz.colorrecipesexplorer:database-manager:1.0") + implementation("dev.fyloz.colorrecipesexplorer:database-manager:1.0.1") implementation("org.springframework.boot:spring-boot-starter-data-jpa:2.3.4.RELEASE") implementation("org.springframework.boot:spring-boot-starter-jdbc:2.3.4.RELEASE") diff --git a/src/main/kotlin/dev/fyloz/trial/colorrecipesexplorer/ColorRecipesExplorerApplication.kt b/src/main/kotlin/dev/fyloz/trial/colorrecipesexplorer/ColorRecipesExplorerApplication.kt index 9bd3ead..15b6483 100644 --- a/src/main/kotlin/dev/fyloz/trial/colorrecipesexplorer/ColorRecipesExplorerApplication.kt +++ b/src/main/kotlin/dev/fyloz/trial/colorrecipesexplorer/ColorRecipesExplorerApplication.kt @@ -2,15 +2,20 @@ package dev.fyloz.trial.colorrecipesexplorer import dev.fyloz.trial.colorrecipesexplorer.config.properties.CREProperties import dev.fyloz.trial.colorrecipesexplorer.config.properties.MaterialTypeProperties +import org.slf4j.Logger import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration +import org.springframework.boot.context.properties.ConfigurationProperties import org.springframework.boot.context.properties.EnableConfigurationProperties +import org.springframework.boot.jdbc.DataSourceBuilder import org.springframework.boot.runApplication +import org.springframework.context.annotation.Bean +import javax.sql.DataSource @SpringBootApplication(exclude = [LiquibaseAutoConfiguration::class]) -@EnableConfigurationProperties(MaterialTypeProperties::class, CREProperties::class) +@EnableConfigurationProperties(MaterialTypeProperties::class, CREProperties::class, DatabaseUpdaterProperties::class) class ColorRecipesExplorerApplication fun main(args: Array) { - runApplication(*args) + runApplication() } diff --git a/src/main/kotlin/dev/fyloz/trial/colorrecipesexplorer/DatabaseVersioning.kt b/src/main/kotlin/dev/fyloz/trial/colorrecipesexplorer/DatabaseVersioning.kt new file mode 100644 index 0000000..c4f7e80 --- /dev/null +++ b/src/main/kotlin/dev/fyloz/trial/colorrecipesexplorer/DatabaseVersioning.kt @@ -0,0 +1,126 @@ +package dev.fyloz.trial.colorrecipesexplorer + +import dev.fyloz.colorrecipesexplorer.databasemanager.CreDatabase +import dev.fyloz.colorrecipesexplorer.databasemanager.databaseContext +import dev.fyloz.colorrecipesexplorer.databasemanager.databaseUpdaterProperties +import org.slf4j.Logger +import org.springframework.boot.context.properties.ConfigurationProperties +import org.springframework.boot.jdbc.DataSourceBuilder +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.core.env.Environment +import javax.sql.DataSource + +const val SUPPORTED_DATABASE_VERSION = 2 +const val ENV_VAR_ENABLE_DATABASE_UPDATE_NAME = "CRE_ENABLE_DB_UPDATE" +val DATABASE_NAME_REGEX = Regex("(\\w+)$") + +@Configuration +class DataSourceConfiguration { + @Bean(name = ["dataSource"]) + @ConfigurationProperties(prefix = "spring.datasource") + fun customDataSource( + logger: Logger, + environment: Environment, + databaseUpdaterProperties: DatabaseUpdaterProperties + ): DataSource { + val databaseUrl: String = environment.getProperty("spring.datasource.url")!! + + runDatabaseVersionCheck(logger, databaseUrl, databaseUpdaterProperties) + + return DataSourceBuilder + .create() + .url(databaseUrl) // Hikari won't start without that + .build() + } +} + +/** + * Runs a database version check. If the database's version is not supported and the environment variable 'CRE_ENABLE_DB_UPDATE' is: + * - 1: The database will be updated. + * - Any other value: The application will not start. + */ +fun runDatabaseVersionCheck(logger: Logger, databaseUrl: String, databaseUpdaterProperties: DatabaseUpdaterProperties) { + logger.info("Verifying database's version before starting...") + + if (":h2:" in databaseUrl) { + logger.warn("H2 is not supported by the database manager") + return + } + + val databaseUpdateEnabled = System.getenv(ENV_VAR_ENABLE_DATABASE_UPDATE_NAME) == "1" + val database = getDatabase(databaseUrl, databaseUpdaterProperties, logger) + val version = database.version + + if (version != SUPPORTED_DATABASE_VERSION) { + if (!databaseUpdateEnabled) { + database.close() + throwUnsupportedDatabaseVersion(version, logger) + } else runDatabaseUpdate(logger, database) + } else { + logger.info("The database is up to date!") + + if (databaseUpdateEnabled) { + logger.warn("The database is up to date but the environment variable '$ENV_VAR_ENABLE_DATABASE_UPDATE_NAME' is set to '1'. Set it to '0' to prevent any possible data loss.") + } + } + + database.close() +} + +/** Updates the given [database]. */ +fun runDatabaseUpdate(logger: Logger, database: CreDatabase) { + logger.info("The environment variable '$ENV_VAR_ENABLE_DATABASE_UPDATE_NAME' is set to '1'; The database will be updated.") + database.update() +} + +fun getDatabase( + databaseUrl: String, + databaseUpdaterProperties: DatabaseUpdaterProperties, + logger: Logger +): CreDatabase { + val databaseName = + (DATABASE_NAME_REGEX.find(databaseUrl) ?: throw DatabaseVersioningException.InvalidUrl(databaseUrl)).value + + return CreDatabase( + databaseContext( + properties = databaseUpdaterProperties( + targetVersion = SUPPORTED_DATABASE_VERSION, + url = databaseUrl.removeSuffix(databaseName), + dbName = databaseName, + username = databaseUpdaterProperties.username, + password = databaseUpdaterProperties.password + ), + logger + ) + ) +} + +fun throwUnsupportedDatabaseVersion(version: Int, logger: Logger) { + if (version > SUPPORTED_DATABASE_VERSION) { + logger.error("Version $version of the database is not supported; Only version $SUPPORTED_DATABASE_VERSION is currently supported; Update this application to use the database.") + } else { + logger.error( + """Version $version of the database is not supported; Only version $SUPPORTED_DATABASE_VERSION is currently supported. + |You can update the database to the supported version by either: + | - Setting the environment variable '$ENV_VAR_ENABLE_DATABASE_UPDATE_NAME' to '1' to update the database automatically + | - Updating the database manually with the database manager utility (https://git.fyloz.dev/color-recipes-explorer/database-manager) + | + |Don't forget to backup the database before upgrading to prevent any data loss. + """.trimMargin() + ) + } + throw DatabaseVersioningException.UnsupportedDatabaseVersion(version) +} + +@ConfigurationProperties(prefix = "database-updater") +class DatabaseUpdaterProperties { + var username: String = "" + var password: String = "" +} + +sealed class DatabaseVersioningException(message: String) : Exception(message) { + class InvalidUrl(url: String) : DatabaseVersioningException("Invalid database url: $url") + class UnsupportedDatabaseVersion(version: Int) : + DatabaseVersioningException("Unsupported database version: $version; Only version $SUPPORTED_DATABASE_VERSION is currently supported") +} diff --git a/src/main/resources/application-h2.properties b/src/main/resources/application-h2.properties index ecf43bb..8b0fa4e 100644 --- a/src/main/resources/application-h2.properties +++ b/src/main/resources/application-h2.properties @@ -1,7 +1,6 @@ spring.datasource.url=jdbc:h2:mem:cre -#spring.datasource.url=jdbc:h2:file:./workdir/recipes spring.datasource.username=sa -spring.datasource.password=LWK4Y7TvEbNyhu1yCoG3 +spring.datasource.password=password spring.jpa.hibernate.ddl-auto=update spring.h2.console.path=/dbconsole spring.h2.console.enabled=true diff --git a/src/main/resources/application-mysql.properties b/src/main/resources/application-mysql.properties index 5830850..4d6c5ff 100644 --- a/src/main/resources/application-mysql.properties +++ b/src/main/resources/application-mysql.properties @@ -1,4 +1,4 @@ -spring.datasource.url=jdbc:mysql://172.20.0.2/cre +spring.datasource.url=jdbc:mysql://172.66.1.1/cre spring.datasource.username=root spring.datasource.password=pass spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 1c41823..e134275 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,7 +1,7 @@ # PORT server.port=9090 # CRE -cre.server.upload-directory=./workdir +cre.server.upload-directory=data cre.server.password-file=passwords.txt cre.server.url-use-port=true cre.server.url-use-https=false @@ -10,10 +10,8 @@ cre.security.jwt-duration=18000000 # Root user cre.security.root.id=9999 cre.security.root.password=password -# Common user -#cre.security.common.id=9998 -#cre.security.common.password=common -# TYPES DE PRODUIT PAR DÉFAUT + +# Default material types entities.material-types.systemTypes[0].name=Aucun entities.material-types.systemTypes[0].prefix= entities.material-types.systemTypes[0].usepercentages=false @@ -21,9 +19,14 @@ entities.material-types.systemTypes[1].name=Base entities.material-types.systemTypes[1].prefix=BAS entities.material-types.systemTypes[1].usepercentages=false entities.material-types.baseName=Base + +# Database manager +database-updater.username=root +database-updater.password=pass + # DEBUG spring.jpa.show-sql=true -# NE PAS MODIFIER +# Do not modify spring.messages.fallback-to-system-locale=true spring.servlet.multipart.max-file-size=10MB spring.servlet.multipart.max-request-size=15MB @@ -31,4 +34,5 @@ spring.jpa.hibernate.ddl-auto=none spring.jpa.open-in-view=true server.http2.enabled=true server.error.whitelabel.enabled=false +spring.h2.console.enabled=false spring.profiles.active=@spring.profiles.active@ diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index 4ddd81b..b5ccfc6 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -1,10 +1,11 @@ + - %black(%d{ISO8601}) %highlight(%-5level) [%blue(%t)] %yellow(%C{1.}): %msg%n%throwable + %black(%d{ISO8601}) %highlight(%-5level) [%blue(%t)] %yellow(%C{36}): %msg%n%throwable @@ -20,7 +21,7 @@ - %d{dd-MM-yyyy HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%M - %msg%n + %d{dd-MM-yyyy HH:mm:ss.SSS} [%thread] %-5level %logger.%M - %msg%n