Compare commits

..

5 Commits

Author SHA1 Message Date
Syer10
3bc75d5b00 Review comments 2026-05-12 09:32:09 -04:00
Syer10
fe6dd05411 Update H2 2026-05-11 19:20:06 -04:00
Syer10
57c0a85a35 Add Kotlinx.DateTime extensions 2026-05-11 00:31:23 -04:00
Syer10
c2c927ae97 Update Exposed 2026-05-11 00:25:44 -04:00
renovate[bot]
2c70700bb7 Update exposed to v1 2026-05-11 04:16:03 +00:00
13 changed files with 55 additions and 155 deletions

View File

@@ -10,7 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
- . - .
### Changed ### Changed
- (Database/H2) Use the latest H2 database engine - .
### Fixed ### Fixed
- (CloudFlareInterceptor) Don't send the `cf_clearance` cookie back to Flaresolverr - (CloudFlareInterceptor) Don't send the `cf_clearance` cookie back to Flaresolverr

View File

@@ -13,6 +13,6 @@ import org.jetbrains.exposed.v1.core.dao.id.IntIdTable
* Metadata storage for clients, server/global level. * Metadata storage for clients, server/global level.
*/ */
object GlobalMetaTable : IntIdTable() { object GlobalMetaTable : IntIdTable() {
val key = varchar("meta_key", 256) val key = varchar("key", 256)
val value = varchar("value", 4096) val value = varchar("value", 4096)
} }

View File

@@ -15,7 +15,7 @@ import suwayomi.tachidesk.manga.model.table.CategoryMetaTable.ref
* Metadata storage for clients, about Category with id == [ref]. * Metadata storage for clients, about Category with id == [ref].
*/ */
object CategoryMetaTable : IntIdTable() { object CategoryMetaTable : IntIdTable() {
val key = varchar("meta_key", 256) val key = varchar("key", 256)
val value = varchar("value", 4096) val value = varchar("value", 4096)
val ref = reference("category_ref", CategoryTable, ReferenceOption.CASCADE) val ref = reference("category_ref", CategoryTable, ReferenceOption.CASCADE)
} }

View File

@@ -26,7 +26,7 @@ import suwayomi.tachidesk.manga.model.table.ChapterMetaTable.ref
* } * }
*/ */
object ChapterMetaTable : IntIdTable() { object ChapterMetaTable : IntIdTable() {
val key = varchar("meta_key", 256) val key = varchar("key", 256)
val value = varchar("value", 4096) val value = varchar("value", 4096)
val ref = reference("chapter_ref", ChapterTable, ReferenceOption.CASCADE) val ref = reference("chapter_ref", ChapterTable, ReferenceOption.CASCADE)
} }

View File

@@ -26,7 +26,7 @@ import suwayomi.tachidesk.manga.model.table.MangaMetaTable.ref
* } * }
*/ */
object MangaMetaTable : IntIdTable() { object MangaMetaTable : IntIdTable() {
val key = varchar("meta_key", 256) val key = varchar("key", 256)
val value = varchar("value", 4096) val value = varchar("value", 4096)
val ref = reference("manga_ref", MangaTable, ReferenceOption.CASCADE) val ref = reference("manga_ref", MangaTable, ReferenceOption.CASCADE)
} }

View File

@@ -14,7 +14,7 @@ import suwayomi.tachidesk.manga.model.table.SourceMetaTable.ref
* Metadata storage for clients, about Source with id == [ref]. * Metadata storage for clients, about Source with id == [ref].
*/ */
object SourceMetaTable : IntIdTable() { object SourceMetaTable : IntIdTable() {
val key = varchar("meta_key", 256) val key = varchar("key", 256)
val value = varchar("value", 4096) val value = varchar("value", 4096)
val ref = long("source_ref") val ref = long("source_ref")
} }

View File

@@ -27,6 +27,7 @@ import suwayomi.tachidesk.server.util.ExitCode
import suwayomi.tachidesk.server.util.shutdownApp import suwayomi.tachidesk.server.util.shutdownApp
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.sql.SQLException
import kotlin.time.Duration.Companion.minutes import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.seconds import kotlin.time.Duration.Companion.seconds
@@ -183,7 +184,7 @@ fun databaseUp() {
} }
val migrations = loadMigrationsFrom("suwayomi.tachidesk.server.database.migration", ServerConfig::class.java) val migrations = loadMigrationsFrom("suwayomi.tachidesk.server.database.migration", ServerConfig::class.java)
runMigrations(migrations) runMigrations(migrations)
} catch (e: Exception) { } catch (e: SQLException) {
logger.error(e) { "Error up-to-database migration" } logger.error(e) { "Error up-to-database migration" }
if (System.getProperty("crashOnFailedMigration").toBoolean()) { if (System.getProperty("crashOnFailedMigration").toBoolean()) {
shutdownApp(ExitCode.DbMigrationFailure) shutdownApp(ExitCode.DbMigrationFailure)

View File

@@ -9,12 +9,9 @@ import java.net.URLClassLoader
import java.nio.file.Path import java.nio.file.Path
import kotlin.io.path.Path import kotlin.io.path.Path
import kotlin.io.path.absolutePathString import kotlin.io.path.absolutePathString
import kotlin.io.path.bufferedReader
import kotlin.io.path.bufferedWriter
import kotlin.io.path.copyTo import kotlin.io.path.copyTo
import kotlin.io.path.createDirectories import kotlin.io.path.createDirectories
import kotlin.io.path.deleteExisting import kotlin.io.path.deleteExisting
import kotlin.io.path.deleteIfExists
import kotlin.io.path.div import kotlin.io.path.div
import kotlin.io.path.exists import kotlin.io.path.exists
import kotlin.io.path.name import kotlin.io.path.name
@@ -49,10 +46,6 @@ object H2Migration {
} }
val script = Path("$dbBase.${h2Old.substringAfterLast('.')}.sql") val script = Path("$dbBase.${h2Old.substringAfterLast('.')}.sql")
script.deleteIfExists()
val modifiedScript = Path("$dbBase.${h2Old.substringAfterLast('.')}.modified.sql")
modifiedScript.deleteIfExists()
// Backup original database. // Backup original database.
val backup = Path("$dbBase.mv.db.${h2Old.substringAfterLast('.')}.backup") val backup = Path("$dbBase.mv.db.${h2Old.substringAfterLast('.')}.backup")
@@ -79,32 +72,19 @@ object H2Migration {
libsDir.resolve("h2-$h2New.bin"), libsDir.resolve("h2-$h2New.bin"),
) )
// Delete attempted migration if failed previously
val newDatabase = Path(rootDir, "database.${h2New.substringAfterLast('.')}.mv.db")
newDatabase.deleteIfExists()
val modifiedNewDatabase = Path(rootDir, "database.${h2Old.substringAfterLast('.')}.modified.${h2New.substringAfterLast('.')}.mv.db")
modifiedNewDatabase.deleteIfExists()
runMigrationTool( runMigrationTool(
migrationJar = migrationJar, migrationJar = migrationJar,
libsDir = libsDir, libsDir = libsDir,
mvStore = mvStore, mvStore = mvStore,
script = script, script = script,
modifiedScript = modifiedScript,
h2Old = h2Old, h2Old = h2Old,
h2New = h2New, h2New = h2New,
) )
// Move database to proper path // Move database to proper path
if (modifiedNewDatabase.exists()) { val newDatabase = Path(rootDir, "database.${h2New.substringAfterLast('.')}.mv.db")
modifiedNewDatabase.copyTo(mvStore, overwrite = true) newDatabase.copyTo(mvStore, overwrite = true)
modifiedNewDatabase.deleteExisting() newDatabase.deleteExisting()
newDatabase.deleteIfExists()
} else {
newDatabase.copyTo(mvStore, overwrite = true)
newDatabase.deleteExisting()
}
logger.info { "H2 migration completed successfully." } logger.info { "H2 migration completed successfully." }
} }
@@ -143,7 +123,6 @@ object H2Migration {
libsDir: Path, libsDir: Path,
mvStore: Path, mvStore: Path,
script: Path, script: Path,
modifiedScript: Path,
h2Old: String, h2Old: String,
h2New: String, h2New: String,
) { ) {
@@ -157,77 +136,32 @@ object H2Migration {
val main = val main =
clazz.getMethod("main", Array<String>::class.java) clazz.getMethod("main", Array<String>::class.java)
try { main.invoke(
main.invoke( null,
null, arrayOf(
arrayOf( // h2 driver dir
// h2 driver dir "-l",
"-l", libsDir.absolutePathString(),
libsDir.absolutePathString(), // from version
// from version "-f",
"-f", h2Old,
h2Old, // to version
// to version "-t",
"-t", h2New,
h2New, // user
// user "-u",
"-u", "",
"", // password
// password "-p",
"-p", "",
"", // database.mv.db
// database.mv.db "-d",
"-d", mvStore.absolutePathString(),
mvStore.absolutePathString(), // database backup in SQL
// database backup in SQL "-s",
"-s", script.absolutePathString(),
script.absolutePathString(), ),
), )
)
} catch (e: Exception) {
// Modify raw .sql file as needed for compatibility
if (e.stackTraceToString().contains("Unknown data type: \"DATETIME\"; SQL statement:") && script.exists()) {
script.bufferedReader().use { reader ->
modifiedScript.bufferedWriter().use { writer ->
reader.forEachLine { line ->
writer.write(
line.replace(
" \"EXECUTED_AT\" DATETIME(9) NOT NULL",
" \"EXECUTED_AT\" TIMESTAMP(9) NOT NULL",
),
)
writer.newLine()
}
}
}
main.invoke(
null,
arrayOf(
// h2 driver dir
"-l",
libsDir.absolutePathString(),
// from version
"-f",
h2Old,
// to version
"-t",
h2New,
// user
"-u",
"",
// password
"-p",
"",
// database.mv.db
"-d",
modifiedScript.absolutePathString(),
),
)
} else {
throw e
}
}
} }
} }
} }

View File

@@ -10,10 +10,17 @@ package suwayomi.tachidesk.server.database.migration
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import de.neonew.exposed.migrations.helpers.SQLMigration import de.neonew.exposed.migrations.helpers.SQLMigration
import suwayomi.tachidesk.server.database.migration.helpers.toSqlName import org.jetbrains.exposed.v1.jdbc.transactions.TransactionManager
@Suppress("ClassName", "unused") @Suppress("ClassName", "unused")
class M0023_CategoryMetaRefFix : SQLMigration() { class M0023_CategoryMetaRefFix : SQLMigration() {
fun String.toSqlName(): String =
TransactionManager.defaultDatabase!!.identifierManager.let {
it.quoteIfNecessary(
it.inProperCase(this),
)
}
private val CategoryMetaTable by lazy { "CategoryMeta".toSqlName() } private val CategoryMetaTable by lazy { "CategoryMeta".toSqlName() }
private val CategoryRefColumn by lazy { "category_ref".toSqlName() } private val CategoryRefColumn by lazy { "category_ref".toSqlName() }
private val CategoryTable by lazy { "Category".toSqlName() } private val CategoryTable by lazy { "Category".toSqlName() }

View File

@@ -8,7 +8,6 @@ package suwayomi.tachidesk.server.database.migration
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import de.neonew.exposed.migrations.helpers.SQLMigration import de.neonew.exposed.migrations.helpers.SQLMigration
import suwayomi.tachidesk.server.database.migration.helpers.toSqlName
@Suppress("ClassName", "unused") @Suppress("ClassName", "unused")
class M0049_FixDuplicatedMetas : SQLMigration() { class M0049_FixDuplicatedMetas : SQLMigration() {
@@ -16,7 +15,7 @@ class M0049_FixDuplicatedMetas : SQLMigration() {
table: String, table: String,
refColumn: String? = null, refColumn: String? = null,
): String { ): String {
val groupBy = listOfNotNull(refColumn, "KEY".toSqlName()).joinToString(", ") val groupBy = listOfNotNull(refColumn, "KEY").joinToString(", ")
return """ return """
DELETE FROM $table DELETE FROM $table
@@ -31,11 +30,10 @@ class M0049_FixDuplicatedMetas : SQLMigration() {
""".trimIndent() """.trimIndent()
} }
override val sql: String by lazy { override val sql: String =
createMigrationForTable("CATEGORYMETA", "CATEGORY_REF") + createMigrationForTable("CATEGORYMETA", "CATEGORY_REF") +
createMigrationForTable("CHAPTERMETA", "CHAPTER_REF") + createMigrationForTable("CHAPTERMETA", "CHAPTER_REF") +
createMigrationForTable("GLOBALMETA") + createMigrationForTable("GLOBALMETA") +
createMigrationForTable("MANGAMETA", "MANGA_REF") + createMigrationForTable("MANGAMETA", "MANGA_REF") +
createMigrationForTable("SOURCEMETA", "SOURCE_REF") createMigrationForTable("SOURCEMETA", "SOURCE_REF")
}
} }

View File

@@ -1,38 +0,0 @@
package suwayomi.tachidesk.server.database.migration
/*
* Copyright (C) Contributors to the Suwayomi project
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import de.neonew.exposed.migrations.helpers.SQLMigration
import suwayomi.tachidesk.graphql.types.DatabaseType
import suwayomi.tachidesk.server.database.migration.helpers.toSqlName
import suwayomi.tachidesk.server.serverConfig
@Suppress("ClassName", "unused")
class M0055_RenameMetaKeys : SQLMigration() {
fun postgresRename(table: String): String =
"ALTER TABLE $table " +
"RENAME COLUMN " + "KEY".toSqlName() + " TO META_KEY;"
fun h2Rename(table: String): String =
"ALTER TABLE $table " +
"ALTER COLUMN " + "KEY".toSqlName() + " RENAME TO META_KEY;"
fun createRenameMigration(table: String): String =
when (serverConfig.databaseType.value) {
DatabaseType.H2 -> h2Rename(table.toSqlName())
DatabaseType.POSTGRESQL -> postgresRename(table.toSqlName())
}
override val sql: String by lazy {
createRenameMigration("CATEGORYMETA") +
createRenameMigration("CHAPTERMETA") +
createRenameMigration("GLOBALMETA") +
createRenameMigration("MANGAMETA") +
createRenameMigration("SOURCEMETA")
}
}

View File

@@ -1,9 +1,17 @@
package suwayomi.tachidesk.server.database.migration.helpers package suwayomi.tachidesk.server.database.migration.helpers
import de.neonew.exposed.migrations.helpers.SQLMigration import de.neonew.exposed.migrations.helpers.SQLMigration
import org.jetbrains.exposed.v1.jdbc.transactions.TransactionManager
import suwayomi.tachidesk.graphql.types.DatabaseType import suwayomi.tachidesk.graphql.types.DatabaseType
import suwayomi.tachidesk.server.serverConfig import suwayomi.tachidesk.server.serverConfig
fun String.toSqlName(): String =
TransactionManager.current().db.identifierManager.let {
it.quoteIfNecessary(
it.inProperCase(this),
)
}
abstract class RenameFieldMigration( abstract class RenameFieldMigration(
tableName: String, tableName: String,
originalName: String, originalName: String,

View File

@@ -1,10 +0,0 @@
package suwayomi.tachidesk.server.database.migration.helpers
import org.jetbrains.exposed.v1.jdbc.transactions.TransactionManager
fun String.toSqlName(): String =
TransactionManager.current().db.identifierManager.let {
it.quoteIfNecessary(
it.inProperCase(this),
)
}