From 63a078cf7dcc36f96723344d7e7d6e7924fcb3ee Mon Sep 17 00:00:00 2001 From: Aria Moradi Date: Thu, 6 May 2021 18:44:46 +0430 Subject: [PATCH] Add migrations (#76) --- server/build.gradle.kts | 9 +- .../ir/armor/tachidesk/impl/Category.kt | 6 +- .../ir/armor/tachidesk/impl/CategoryManga.kt | 8 +- .../kotlin/ir/armor/tachidesk/impl/Chapter.kt | 6 +- .../ir/armor/tachidesk/impl/Extension.kt | 4 +- .../ir/armor/tachidesk/impl/ExtensionsList.kt | 2 +- .../kotlin/ir/armor/tachidesk/impl/Library.kt | 6 +- .../kotlin/ir/armor/tachidesk/impl/Manga.kt | 4 +- .../ir/armor/tachidesk/impl/MangaList.kt | 4 +- .../kotlin/ir/armor/tachidesk/impl/Page.kt | 8 +- .../kotlin/ir/armor/tachidesk/impl/Source.kt | 4 +- .../impl/backup/legacy/LegacyBackupExport.kt | 4 +- .../impl/backup/legacy/LegacyBackupImport.kt | 2 +- .../backup/legacy/LegacyBackupValidator.kt | 2 +- .../impl/backup/models/ChapterImpl.kt | 2 +- .../tachidesk/impl/backup/models/MangaImpl.kt | 2 +- .../tachidesk/impl/util/GetHttpSource.kt | 4 +- .../tachidesk/model/database/DBMangaer.kt | 19 +-- .../model/database/migration/M0001_Initial.kt | 117 ++++++++++++++++++ .../model/database/migration/lib/LICENSE | 21 ++++ .../model/database/migration/lib/Migration.kt | 25 ++++ .../database/migration/lib/MigrationEntity.kt | 37 ++++++ .../database/migration/lib/runMigrations.kt | 97 +++++++++++++++ .../{ => table}/CategoryMangaTable.kt | 2 +- .../database/{ => table}/CategoryTable.kt | 2 +- .../database/{ => table}/ChapterTable.kt | 2 +- .../database/{ => table}/ExtensionTable.kt | 2 +- .../model/database/{ => table}/MangaTable.kt | 4 +- .../model/database/{ => table}/PageTable.kt | 2 +- .../model/database/{ => table}/SourceTable.kt | 2 +- .../model/dataclass/MangaDataClass.kt | 2 +- .../ir/armor/tachidesk/server/ServerSetup.kt | 4 +- 32 files changed, 352 insertions(+), 63 deletions(-) create mode 100644 server/src/main/kotlin/ir/armor/tachidesk/model/database/migration/M0001_Initial.kt create mode 100644 server/src/main/kotlin/ir/armor/tachidesk/model/database/migration/lib/LICENSE create mode 100644 server/src/main/kotlin/ir/armor/tachidesk/model/database/migration/lib/Migration.kt create mode 100644 server/src/main/kotlin/ir/armor/tachidesk/model/database/migration/lib/MigrationEntity.kt create mode 100644 server/src/main/kotlin/ir/armor/tachidesk/model/database/migration/lib/runMigrations.kt rename server/src/main/kotlin/ir/armor/tachidesk/model/database/{ => table}/CategoryMangaTable.kt (90%) rename server/src/main/kotlin/ir/armor/tachidesk/model/database/{ => table}/CategoryTable.kt (94%) rename server/src/main/kotlin/ir/armor/tachidesk/model/database/{ => table}/ChapterTable.kt (94%) rename server/src/main/kotlin/ir/armor/tachidesk/model/database/{ => table}/ExtensionTable.kt (96%) rename server/src/main/kotlin/ir/armor/tachidesk/model/database/{ => table}/MangaTable.kt (95%) rename server/src/main/kotlin/ir/armor/tachidesk/model/database/{ => table}/PageTable.kt (91%) rename server/src/main/kotlin/ir/armor/tachidesk/model/database/{ => table}/SourceTable.kt (92%) diff --git a/server/build.gradle.kts b/server/build.gradle.kts index 686bbe4..e11cfaa 100644 --- a/server/build.gradle.kts +++ b/server/build.gradle.kts @@ -20,9 +20,6 @@ repositories { } dependencies { -// implementation(platform("org.jetbrains.kotlin:kotlin-bom")) - implementation(kotlin("stdlib-jdk8")) - // Source models and interfaces from Tachiyomi 1.x // using source class from tachiyomi commit 9493577de27c40ce8b2b6122cc447d025e34c477 to not depend on tachiyomi.sourceapi // implementation("tachiyomi.sourceapi:source-api:1.1") @@ -47,7 +44,7 @@ dependencies { // Reactivex implementation("io.reactivex:rxjava:1.3.8") - implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.0") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.1.0") implementation("com.google.code.gson:gson:2.8.6") implementation("com.github.salomonbrys.kotson:kotson:2.5.0") @@ -64,12 +61,16 @@ dependencies { implementation("org.jetbrains.exposed:exposed-core:$exposedVersion") implementation("org.jetbrains.exposed:exposed-dao:$exposedVersion") implementation("org.jetbrains.exposed:exposed-jdbc:$exposedVersion") + implementation("org.jetbrains.exposed:exposed-java-time:$exposedVersion") + + // current database driver implementation("com.h2database:h2:1.4.200") // tray icon implementation("com.dorkbox:SystemTray:4.1") implementation("com.dorkbox:Utilities:1.9") + implementation("com.google.guava:guava:30.1.1-jre") // AndroidCompat implementation(project(":AndroidCompat")) diff --git a/server/src/main/kotlin/ir/armor/tachidesk/impl/Category.kt b/server/src/main/kotlin/ir/armor/tachidesk/impl/Category.kt index 2a0f0bc..81599c9 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/impl/Category.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/impl/Category.kt @@ -8,9 +8,9 @@ package ir.armor.tachidesk.impl * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import ir.armor.tachidesk.impl.CategoryManga.removeMangaFromCategory -import ir.armor.tachidesk.model.database.CategoryMangaTable -import ir.armor.tachidesk.model.database.CategoryTable -import ir.armor.tachidesk.model.database.toDataClass +import ir.armor.tachidesk.model.database.table.CategoryMangaTable +import ir.armor.tachidesk.model.database.table.CategoryTable +import ir.armor.tachidesk.model.database.table.toDataClass import ir.armor.tachidesk.model.dataclass.CategoryDataClass import org.jetbrains.exposed.sql.SortOrder import org.jetbrains.exposed.sql.deleteWhere diff --git a/server/src/main/kotlin/ir/armor/tachidesk/impl/CategoryManga.kt b/server/src/main/kotlin/ir/armor/tachidesk/impl/CategoryManga.kt index 1b9817f..948eee5 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/impl/CategoryManga.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/impl/CategoryManga.kt @@ -7,10 +7,10 @@ package ir.armor.tachidesk.impl * 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 ir.armor.tachidesk.model.database.CategoryMangaTable -import ir.armor.tachidesk.model.database.CategoryTable -import ir.armor.tachidesk.model.database.MangaTable -import ir.armor.tachidesk.model.database.toDataClass +import ir.armor.tachidesk.model.database.table.CategoryMangaTable +import ir.armor.tachidesk.model.database.table.CategoryTable +import ir.armor.tachidesk.model.database.table.MangaTable +import ir.armor.tachidesk.model.database.table.toDataClass import ir.armor.tachidesk.model.dataclass.CategoryDataClass import ir.armor.tachidesk.model.dataclass.MangaDataClass import org.jetbrains.exposed.sql.SortOrder diff --git a/server/src/main/kotlin/ir/armor/tachidesk/impl/Chapter.kt b/server/src/main/kotlin/ir/armor/tachidesk/impl/Chapter.kt index 2070d70..0456b46 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/impl/Chapter.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/impl/Chapter.kt @@ -12,9 +12,9 @@ import eu.kanade.tachiyomi.source.model.SManga import ir.armor.tachidesk.impl.Manga.getManga import ir.armor.tachidesk.impl.util.GetHttpSource.getHttpSource import ir.armor.tachidesk.impl.util.awaitSingle -import ir.armor.tachidesk.model.database.ChapterTable -import ir.armor.tachidesk.model.database.MangaTable -import ir.armor.tachidesk.model.database.PageTable +import ir.armor.tachidesk.model.database.table.ChapterTable +import ir.armor.tachidesk.model.database.table.MangaTable +import ir.armor.tachidesk.model.database.table.PageTable import ir.armor.tachidesk.model.dataclass.ChapterDataClass import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.insert diff --git a/server/src/main/kotlin/ir/armor/tachidesk/impl/Extension.kt b/server/src/main/kotlin/ir/armor/tachidesk/impl/Extension.kt index dced2e5..02fc24d 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/impl/Extension.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/impl/Extension.kt @@ -27,8 +27,8 @@ import ir.armor.tachidesk.impl.util.PackageTools.getSignatureHash import ir.armor.tachidesk.impl.util.PackageTools.loadExtensionSources import ir.armor.tachidesk.impl.util.PackageTools.trustedSignatures import ir.armor.tachidesk.impl.util.await -import ir.armor.tachidesk.model.database.ExtensionTable -import ir.armor.tachidesk.model.database.SourceTable +import ir.armor.tachidesk.model.database.table.ExtensionTable +import ir.armor.tachidesk.model.database.table.SourceTable import ir.armor.tachidesk.server.ApplicationDirs import mu.KotlinLogging import okhttp3.Request diff --git a/server/src/main/kotlin/ir/armor/tachidesk/impl/ExtensionsList.kt b/server/src/main/kotlin/ir/armor/tachidesk/impl/ExtensionsList.kt index a72b5d2..c373e54 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/impl/ExtensionsList.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/impl/ExtensionsList.kt @@ -10,7 +10,7 @@ package ir.armor.tachidesk.impl import eu.kanade.tachiyomi.extension.api.ExtensionGithubApi import eu.kanade.tachiyomi.extension.model.Extension import ir.armor.tachidesk.impl.Extension.getExtensionIconUrl -import ir.armor.tachidesk.model.database.ExtensionTable +import ir.armor.tachidesk.model.database.table.ExtensionTable import ir.armor.tachidesk.model.dataclass.ExtensionDataClass import mu.KotlinLogging import org.jetbrains.exposed.sql.deleteWhere diff --git a/server/src/main/kotlin/ir/armor/tachidesk/impl/Library.kt b/server/src/main/kotlin/ir/armor/tachidesk/impl/Library.kt index ea2c942..6bffb82 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/impl/Library.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/impl/Library.kt @@ -8,9 +8,9 @@ package ir.armor.tachidesk.impl * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import ir.armor.tachidesk.impl.Manga.getManga -import ir.armor.tachidesk.model.database.CategoryMangaTable -import ir.armor.tachidesk.model.database.MangaTable -import ir.armor.tachidesk.model.database.toDataClass +import ir.armor.tachidesk.model.database.table.CategoryMangaTable +import ir.armor.tachidesk.model.database.table.MangaTable +import ir.armor.tachidesk.model.database.table.toDataClass import ir.armor.tachidesk.model.dataclass.MangaDataClass import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.deleteWhere diff --git a/server/src/main/kotlin/ir/armor/tachidesk/impl/Manga.kt b/server/src/main/kotlin/ir/armor/tachidesk/impl/Manga.kt index ee08087..106818f 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/impl/Manga.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/impl/Manga.kt @@ -15,8 +15,8 @@ import ir.armor.tachidesk.impl.util.CachedImageResponse.getCachedImageResponse import ir.armor.tachidesk.impl.util.GetHttpSource.getHttpSource import ir.armor.tachidesk.impl.util.await import ir.armor.tachidesk.impl.util.awaitSingle -import ir.armor.tachidesk.model.database.MangaStatus -import ir.armor.tachidesk.model.database.MangaTable +import ir.armor.tachidesk.model.database.table.MangaStatus +import ir.armor.tachidesk.model.database.table.MangaTable import ir.armor.tachidesk.model.dataclass.MangaDataClass import ir.armor.tachidesk.server.ApplicationDirs import org.jetbrains.exposed.sql.select diff --git a/server/src/main/kotlin/ir/armor/tachidesk/impl/MangaList.kt b/server/src/main/kotlin/ir/armor/tachidesk/impl/MangaList.kt index dfbeec5..94a632a 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/impl/MangaList.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/impl/MangaList.kt @@ -10,8 +10,8 @@ package ir.armor.tachidesk.impl import eu.kanade.tachiyomi.source.model.MangasPage import ir.armor.tachidesk.impl.util.GetHttpSource.getHttpSource import ir.armor.tachidesk.impl.util.awaitSingle -import ir.armor.tachidesk.model.database.MangaStatus -import ir.armor.tachidesk.model.database.MangaTable +import ir.armor.tachidesk.model.database.table.MangaStatus +import ir.armor.tachidesk.model.database.table.MangaTable import ir.armor.tachidesk.model.dataclass.MangaDataClass import ir.armor.tachidesk.model.dataclass.PagedMangaListDataClass import org.jetbrains.exposed.sql.insertAndGetId diff --git a/server/src/main/kotlin/ir/armor/tachidesk/impl/Page.kt b/server/src/main/kotlin/ir/armor/tachidesk/impl/Page.kt index 299ad8f..712f5ab 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/impl/Page.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/impl/Page.kt @@ -12,10 +12,10 @@ import eu.kanade.tachiyomi.source.online.HttpSource import ir.armor.tachidesk.impl.util.CachedImageResponse.getCachedImageResponse import ir.armor.tachidesk.impl.util.GetHttpSource.getHttpSource import ir.armor.tachidesk.impl.util.awaitSingle -import ir.armor.tachidesk.model.database.ChapterTable -import ir.armor.tachidesk.model.database.MangaTable -import ir.armor.tachidesk.model.database.PageTable -import ir.armor.tachidesk.model.database.SourceTable +import ir.armor.tachidesk.model.database.table.ChapterTable +import ir.armor.tachidesk.model.database.table.MangaTable +import ir.armor.tachidesk.model.database.table.PageTable +import ir.armor.tachidesk.model.database.table.SourceTable import ir.armor.tachidesk.server.ApplicationDirs import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.select diff --git a/server/src/main/kotlin/ir/armor/tachidesk/impl/Source.kt b/server/src/main/kotlin/ir/armor/tachidesk/impl/Source.kt index 9fec2bf..0852ddd 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/impl/Source.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/impl/Source.kt @@ -9,8 +9,8 @@ package ir.armor.tachidesk.impl import ir.armor.tachidesk.impl.Extension.getExtensionIconUrl import ir.armor.tachidesk.impl.util.GetHttpSource.getHttpSource -import ir.armor.tachidesk.model.database.ExtensionTable -import ir.armor.tachidesk.model.database.SourceTable +import ir.armor.tachidesk.model.database.table.ExtensionTable +import ir.armor.tachidesk.model.database.table.SourceTable import ir.armor.tachidesk.model.dataclass.SourceDataClass import mu.KotlinLogging import org.jetbrains.exposed.sql.select diff --git a/server/src/main/kotlin/ir/armor/tachidesk/impl/backup/legacy/LegacyBackupExport.kt b/server/src/main/kotlin/ir/armor/tachidesk/impl/backup/legacy/LegacyBackupExport.kt index e658745..c8593b4 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/impl/backup/legacy/LegacyBackupExport.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/impl/backup/legacy/LegacyBackupExport.kt @@ -22,8 +22,8 @@ import ir.armor.tachidesk.impl.backup.models.ChapterImpl import ir.armor.tachidesk.impl.backup.models.Manga import ir.armor.tachidesk.impl.backup.models.MangaImpl import ir.armor.tachidesk.impl.util.GetHttpSource.getHttpSource -import ir.armor.tachidesk.model.database.ChapterTable -import ir.armor.tachidesk.model.database.MangaTable +import ir.armor.tachidesk.model.database.table.ChapterTable +import ir.armor.tachidesk.model.database.table.MangaTable import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.transactions.transaction diff --git a/server/src/main/kotlin/ir/armor/tachidesk/impl/backup/legacy/LegacyBackupImport.kt b/server/src/main/kotlin/ir/armor/tachidesk/impl/backup/legacy/LegacyBackupImport.kt index 588c6ad..545f885 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/impl/backup/legacy/LegacyBackupImport.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/impl/backup/legacy/LegacyBackupImport.kt @@ -22,7 +22,7 @@ import ir.armor.tachidesk.impl.backup.models.Track import ir.armor.tachidesk.impl.backup.models.TrackImpl import ir.armor.tachidesk.impl.util.GetHttpSource.getHttpSource import ir.armor.tachidesk.impl.util.awaitSingle -import ir.armor.tachidesk.model.database.MangaTable +import ir.armor.tachidesk.model.database.table.MangaTable import mu.KotlinLogging import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.insert diff --git a/server/src/main/kotlin/ir/armor/tachidesk/impl/backup/legacy/LegacyBackupValidator.kt b/server/src/main/kotlin/ir/armor/tachidesk/impl/backup/legacy/LegacyBackupValidator.kt index 1a97262..a50ff06 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/impl/backup/legacy/LegacyBackupValidator.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/impl/backup/legacy/LegacyBackupValidator.kt @@ -9,7 +9,7 @@ package ir.armor.tachidesk.impl.backup.legacy import com.google.gson.JsonObject import ir.armor.tachidesk.impl.backup.legacy.models.Backup -import ir.armor.tachidesk.model.database.SourceTable +import ir.armor.tachidesk.model.database.table.SourceTable import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.transactions.transaction diff --git a/server/src/main/kotlin/ir/armor/tachidesk/impl/backup/models/ChapterImpl.kt b/server/src/main/kotlin/ir/armor/tachidesk/impl/backup/models/ChapterImpl.kt index d1493f3..3b8edad 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/impl/backup/models/ChapterImpl.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/impl/backup/models/ChapterImpl.kt @@ -1,6 +1,6 @@ package ir.armor.tachidesk.impl.backup.models -import ir.armor.tachidesk.model.database.ChapterTable +import ir.armor.tachidesk.model.database.table.ChapterTable import org.jetbrains.exposed.sql.ResultRow class ChapterImpl : Chapter { diff --git a/server/src/main/kotlin/ir/armor/tachidesk/impl/backup/models/MangaImpl.kt b/server/src/main/kotlin/ir/armor/tachidesk/impl/backup/models/MangaImpl.kt index a39a29c..13e95ef 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/impl/backup/models/MangaImpl.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/impl/backup/models/MangaImpl.kt @@ -1,6 +1,6 @@ package ir.armor.tachidesk.impl.backup.models -import ir.armor.tachidesk.model.database.MangaTable +import ir.armor.tachidesk.model.database.table.MangaTable import org.jetbrains.exposed.sql.ResultRow open class MangaImpl : Manga { diff --git a/server/src/main/kotlin/ir/armor/tachidesk/impl/util/GetHttpSource.kt b/server/src/main/kotlin/ir/armor/tachidesk/impl/util/GetHttpSource.kt index 0c01ea9..7c93a08 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/impl/util/GetHttpSource.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/impl/util/GetHttpSource.kt @@ -11,8 +11,8 @@ import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.SourceFactory import eu.kanade.tachiyomi.source.online.HttpSource import ir.armor.tachidesk.impl.util.PackageTools.loadExtensionSources -import ir.armor.tachidesk.model.database.ExtensionTable -import ir.armor.tachidesk.model.database.SourceTable +import ir.armor.tachidesk.model.database.table.ExtensionTable +import ir.armor.tachidesk.model.database.table.SourceTable import ir.armor.tachidesk.server.ApplicationDirs import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.transactions.transaction diff --git a/server/src/main/kotlin/ir/armor/tachidesk/model/database/DBMangaer.kt b/server/src/main/kotlin/ir/armor/tachidesk/model/database/DBMangaer.kt index 995b3e2..3ced00f 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/model/database/DBMangaer.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/model/database/DBMangaer.kt @@ -7,10 +7,10 @@ package ir.armor.tachidesk.model.database * 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 ir.armor.tachidesk.model.database.migration.lib.loadMigrationsFrom +import ir.armor.tachidesk.model.database.migration.lib.runMigrations import ir.armor.tachidesk.server.ApplicationDirs import org.jetbrains.exposed.sql.Database -import org.jetbrains.exposed.sql.SchemaUtils -import org.jetbrains.exposed.sql.transactions.transaction import org.kodein.di.DI import org.kodein.di.conf.global import org.kodein.di.instance @@ -22,20 +22,11 @@ object DBMangaer { } } -fun makeDataBaseTables() { +fun databaseUp() { // must mention db object so the lazy block executes val db = DBMangaer.db db.useNestedTransactions = true - transaction { - SchemaUtils.createMissingTablesAndColumns( - ExtensionTable, - SourceTable, - MangaTable, - ChapterTable, - PageTable, - CategoryTable, - CategoryMangaTable, - ) - } + val migrations = loadMigrationsFrom("ir.armor.tachidesk.model.database.migration") + runMigrations(migrations) } diff --git a/server/src/main/kotlin/ir/armor/tachidesk/model/database/migration/M0001_Initial.kt b/server/src/main/kotlin/ir/armor/tachidesk/model/database/migration/M0001_Initial.kt new file mode 100644 index 0000000..59e743b --- /dev/null +++ b/server/src/main/kotlin/ir/armor/tachidesk/model/database/migration/M0001_Initial.kt @@ -0,0 +1,117 @@ +package ir.armor.tachidesk.model.database.migration + +import eu.kanade.tachiyomi.source.model.SManga +import ir.armor.tachidesk.model.database.migration.lib.Migration +import org.jetbrains.exposed.dao.id.IdTable +import org.jetbrains.exposed.dao.id.IntIdTable +import org.jetbrains.exposed.sql.SchemaUtils +import org.jetbrains.exposed.sql.transactions.transaction + +/* + * 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/. */ + +class M0001_Initial : Migration() { + private object ExtensionTable : IntIdTable() { + val apkName = varchar("apk_name", 1024) + + // default is the local source icon from tachiyomi + val iconUrl = varchar("icon_url", 2048) + .default("https://raw.githubusercontent.com/tachiyomiorg/tachiyomi/64ba127e7d43b1d7e6d58a6f5c9b2bd5fe0543f7/app/src/main/res/mipmap-xxxhdpi/ic_local_source.webp") + + val name = varchar("name", 128) + val pkgName = varchar("pkg_name", 128) + val versionName = varchar("version_name", 16) + val versionCode = integer("version_code") + val lang = varchar("lang", 10) + val isNsfw = bool("is_nsfw") + + val isInstalled = bool("is_installed").default(false) + val hasUpdate = bool("has_update").default(false) + val isObsolete = bool("is_obsolete").default(false) + + val classFQName = varchar("class_name", 1024).default("") // fully qualified name + } + + private object SourceTable : IdTable() { + override val id = long("id").entityId() + val name = varchar("name", 128) + val lang = varchar("lang", 10) + val extension = reference("extension", ExtensionTable) + val partOfFactorySource = bool("part_of_factory_source").default(false) + } + + private object MangaTable : IntIdTable() { + val url = varchar("url", 2048) + val title = varchar("title", 512) + val initialized = bool("initialized").default(false) + + val artist = varchar("artist", 64).nullable() + val author = varchar("author", 64).nullable() + val description = varchar("description", 4096).nullable() + val genre = varchar("genre", 1024).nullable() + + // val status = enumeration("status", MangaStatus::class).default(MangaStatus.UNKNOWN) + val status = integer("status").default(SManga.UNKNOWN) + val thumbnail_url = varchar("thumbnail_url", 2048).nullable() + + val inLibrary = bool("in_library").default(false) + val defaultCategory = bool("default_category").default(true) + + // source is used by some ancestor of IntIdTable + val sourceReference = long("source") + } + + private object ChapterTable : IntIdTable() { + val url = varchar("url", 2048) + val name = varchar("name", 512) + val date_upload = long("date_upload").default(0) + val chapter_number = float("chapter_number").default(-1f) + val scanlator = varchar("scanlator", 128).nullable() + + val isRead = bool("read").default(false) + val isBookmarked = bool("bookmark").default(false) + val lastPageRead = integer("last_page_read").default(0) + + val chapterIndex = integer("number_in_list") + + val manga = reference("manga", MangaTable) + } + + private object PageTable : IntIdTable() { + val index = integer("index") + val url = varchar("url", 2048) + val imageUrl = varchar("imageUrl", 2048).nullable() + + val chapter = reference("chapter", ChapterTable) + } + + private object CategoryTable : IntIdTable() { + val name = varchar("name", 64) + val isLanding = bool("is_landing").default(false) + val order = integer("order").default(0) + } + + private object CategoryMangaTable : IntIdTable() { + val category = reference("category", ir.armor.tachidesk.model.database.table.CategoryTable) + val manga = reference("manga", ir.armor.tachidesk.model.database.table.MangaTable) + } + + override fun run() { + transaction { + SchemaUtils.create( + ExtensionTable, + ExtensionTable, + SourceTable, + MangaTable, + ChapterTable, + PageTable, + CategoryTable, + CategoryMangaTable, + ) + } + } +} diff --git a/server/src/main/kotlin/ir/armor/tachidesk/model/database/migration/lib/LICENSE b/server/src/main/kotlin/ir/armor/tachidesk/model/database/migration/lib/LICENSE new file mode 100644 index 0000000..bb6a909 --- /dev/null +++ b/server/src/main/kotlin/ir/armor/tachidesk/model/database/migration/lib/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Andreas Mausch + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/server/src/main/kotlin/ir/armor/tachidesk/model/database/migration/lib/Migration.kt b/server/src/main/kotlin/ir/armor/tachidesk/model/database/migration/lib/Migration.kt new file mode 100644 index 0000000..6fe3632 --- /dev/null +++ b/server/src/main/kotlin/ir/armor/tachidesk/model/database/migration/lib/Migration.kt @@ -0,0 +1,25 @@ +package ir.armor.tachidesk.model.database.migration.lib + +/* + * 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/. */ + +// originally licenced under MIT by Andreas Mausch, Changes are licenced under Mozilla Public License, v. 2.0. +// adopted from: https://gitlab.com/andreas-mausch/exposed-migrations/-/tree/4bf853c18a24d0170eda896ddbb899cb01233595 + +abstract class Migration { + val name: String + val version: Int + + init { + val groups = Regex("^M(\\d+)_(.*)$").matchEntire(this::class.simpleName!!)?.groupValues + ?: throw IllegalArgumentException("Migration class name doesn't match convention") + version = groups[1].toInt() + name = groups[2] + } + + abstract fun run() +} diff --git a/server/src/main/kotlin/ir/armor/tachidesk/model/database/migration/lib/MigrationEntity.kt b/server/src/main/kotlin/ir/armor/tachidesk/model/database/migration/lib/MigrationEntity.kt new file mode 100644 index 0000000..6af9f48 --- /dev/null +++ b/server/src/main/kotlin/ir/armor/tachidesk/model/database/migration/lib/MigrationEntity.kt @@ -0,0 +1,37 @@ +package ir.armor.tachidesk.model.database.migration.lib + +/* + * 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/. */ + +// originally licenced under MIT by Andreas Mausch, Changes are licenced under Mozilla Public License, v. 2.0. +// adopted from: https://gitlab.com/andreas-mausch/exposed-migrations/-/tree/4bf853c18a24d0170eda896ddbb899cb01233595 + +import org.jetbrains.exposed.dao.IntEntity +import org.jetbrains.exposed.dao.IntEntityClass +import org.jetbrains.exposed.dao.id.EntityID +import org.jetbrains.exposed.dao.id.IdTable +import org.jetbrains.exposed.sql.`java-time`.timestamp + +object MigrationsTable : IdTable() { + override val id = integer("version").entityId() + override val primaryKey = PrimaryKey(id) + + val name = varchar("name", length = 400) + val executedAt = timestamp("executed_at") + + init { + index(true, name) + } +} + +class MigrationEntity(id: EntityID) : IntEntity(id) { + companion object : IntEntityClass(MigrationsTable) + + var version by MigrationsTable.id + var name by MigrationsTable.name + var executedAt by MigrationsTable.executedAt +} diff --git a/server/src/main/kotlin/ir/armor/tachidesk/model/database/migration/lib/runMigrations.kt b/server/src/main/kotlin/ir/armor/tachidesk/model/database/migration/lib/runMigrations.kt new file mode 100644 index 0000000..4579e90 --- /dev/null +++ b/server/src/main/kotlin/ir/armor/tachidesk/model/database/migration/lib/runMigrations.kt @@ -0,0 +1,97 @@ +package ir.armor.tachidesk.model.database.migration.lib + +/* + * 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/. */ + +// originally licenced under MIT by Andreas Mausch, Changes are licenced under Mozilla Public License, v. 2.0. +// adopted from: https://gitlab.com/andreas-mausch/exposed-migrations/-/tree/4bf853c18a24d0170eda896ddbb899cb01233595 + +import com.google.common.reflect.ClassPath +import mu.KotlinLogging +import org.jetbrains.exposed.dao.id.EntityID +import org.jetbrains.exposed.sql.Database +import org.jetbrains.exposed.sql.SchemaUtils.create +import org.jetbrains.exposed.sql.exists +import org.jetbrains.exposed.sql.transactions.TransactionManager +import org.jetbrains.exposed.sql.transactions.transaction +import java.time.Clock +import java.time.Instant.now + +private val logger = KotlinLogging.logger {} + +fun runMigrations(migrations: List, database: Database = TransactionManager.defaultDatabase!!, clock: Clock = Clock.systemUTC()) { + checkVersions(migrations) + + logger.info { "Running migrations on database ${database.url}" } + + val latestVersion = transaction(database) { + createTableIfNotExists(database) + MigrationEntity.all().maxByOrNull { it.version }?.version?.value + } + + logger.info { "Database version before migrations: $latestVersion" } + + migrations + .sortedBy { it.version } + .filter { shouldRun(latestVersion, it) } + .forEach { + logger.info { "Running migration version ${it.version}: ${it.name}" } + transaction(database) { + it.run() + + MigrationEntity.new { + version = EntityID(it.version, MigrationsTable) + name = it.name + executedAt = now(clock) + } + } + } + + logger.info { "Migrations finished successfully" } +} + +fun loadMigrationsFrom(classPath: String): List { + return ClassPath.from(Thread.currentThread().contextClassLoader) + .getTopLevelClasses(classPath) + .map { + logger.debug("found Migration class ${it.name}") + val clazz = it.load().getDeclaredConstructor().newInstance() + if (clazz is Migration) + clazz + else + throw RuntimeException("found a class that's not a Migration") + } +} + +private fun checkVersions(migrations: List) { + val sorted = migrations.map { it.version }.sorted() + if ((1..migrations.size).toList() != sorted) { + throw IllegalStateException("List of migrations version is not consecutive: $sorted") + } +} + +private fun createTableIfNotExists(database: Database) { + if (MigrationsTable.exists()) { + return + } + val tableNames = database.dialect.allTablesNames() + when (tableNames.isEmpty()) { + true -> { + logger.info { "Empty database found, creating table for migrations" } + create(MigrationsTable) + } + false -> throw IllegalStateException("Tried to run migrations against a non-empty database without a Migrations table. This is not supported.") + } +} + +private fun shouldRun(latestVersion: Int?, migration: Migration): Boolean { + val run = latestVersion?.let { migration.version > it } ?: true + if (!run) { + logger.debug { "Skipping migration version ${migration.version}: ${migration.name}" } + } + return run +} diff --git a/server/src/main/kotlin/ir/armor/tachidesk/model/database/CategoryMangaTable.kt b/server/src/main/kotlin/ir/armor/tachidesk/model/database/table/CategoryMangaTable.kt similarity index 90% rename from server/src/main/kotlin/ir/armor/tachidesk/model/database/CategoryMangaTable.kt rename to server/src/main/kotlin/ir/armor/tachidesk/model/database/table/CategoryMangaTable.kt index 000e65e..b7c79a7 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/model/database/CategoryMangaTable.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/model/database/table/CategoryMangaTable.kt @@ -1,4 +1,4 @@ -package ir.armor.tachidesk.model.database +package ir.armor.tachidesk.model.database.table /* * Copyright (C) Contributors to the Suwayomi project diff --git a/server/src/main/kotlin/ir/armor/tachidesk/model/database/CategoryTable.kt b/server/src/main/kotlin/ir/armor/tachidesk/model/database/table/CategoryTable.kt similarity index 94% rename from server/src/main/kotlin/ir/armor/tachidesk/model/database/CategoryTable.kt rename to server/src/main/kotlin/ir/armor/tachidesk/model/database/table/CategoryTable.kt index 2eda677..3b047a2 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/model/database/CategoryTable.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/model/database/table/CategoryTable.kt @@ -1,4 +1,4 @@ -package ir.armor.tachidesk.model.database +package ir.armor.tachidesk.model.database.table /* * Copyright (C) Contributors to the Suwayomi project diff --git a/server/src/main/kotlin/ir/armor/tachidesk/model/database/ChapterTable.kt b/server/src/main/kotlin/ir/armor/tachidesk/model/database/table/ChapterTable.kt similarity index 94% rename from server/src/main/kotlin/ir/armor/tachidesk/model/database/ChapterTable.kt rename to server/src/main/kotlin/ir/armor/tachidesk/model/database/table/ChapterTable.kt index 0c8119e..30208ab 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/model/database/ChapterTable.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/model/database/table/ChapterTable.kt @@ -1,4 +1,4 @@ -package ir.armor.tachidesk.model.database +package ir.armor.tachidesk.model.database.table /* * Copyright (C) Contributors to the Suwayomi project diff --git a/server/src/main/kotlin/ir/armor/tachidesk/model/database/ExtensionTable.kt b/server/src/main/kotlin/ir/armor/tachidesk/model/database/table/ExtensionTable.kt similarity index 96% rename from server/src/main/kotlin/ir/armor/tachidesk/model/database/ExtensionTable.kt rename to server/src/main/kotlin/ir/armor/tachidesk/model/database/table/ExtensionTable.kt index 2ee5ebf..79bfba7 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/model/database/ExtensionTable.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/model/database/table/ExtensionTable.kt @@ -1,4 +1,4 @@ -package ir.armor.tachidesk.model.database +package ir.armor.tachidesk.model.database.table /* * Copyright (C) Contributors to the Suwayomi project diff --git a/server/src/main/kotlin/ir/armor/tachidesk/model/database/MangaTable.kt b/server/src/main/kotlin/ir/armor/tachidesk/model/database/table/MangaTable.kt similarity index 95% rename from server/src/main/kotlin/ir/armor/tachidesk/model/database/MangaTable.kt rename to server/src/main/kotlin/ir/armor/tachidesk/model/database/table/MangaTable.kt index 2f640c6..fd3fd2b 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/model/database/MangaTable.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/model/database/table/MangaTable.kt @@ -1,4 +1,4 @@ -package ir.armor.tachidesk.model.database +package ir.armor.tachidesk.model.database.table /* * Copyright (C) Contributors to the Suwayomi project @@ -37,7 +37,7 @@ object MangaTable : IntIdTable() { fun MangaTable.toDataClass(mangaEntry: ResultRow) = MangaDataClass( mangaEntry[MangaTable.id].value, - mangaEntry[sourceReference].toString(), + mangaEntry[MangaTable.sourceReference].toString(), mangaEntry[MangaTable.url], mangaEntry[MangaTable.title], diff --git a/server/src/main/kotlin/ir/armor/tachidesk/model/database/PageTable.kt b/server/src/main/kotlin/ir/armor/tachidesk/model/database/table/PageTable.kt similarity index 91% rename from server/src/main/kotlin/ir/armor/tachidesk/model/database/PageTable.kt rename to server/src/main/kotlin/ir/armor/tachidesk/model/database/table/PageTable.kt index 4cf79b6..000cd24 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/model/database/PageTable.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/model/database/table/PageTable.kt @@ -1,4 +1,4 @@ -package ir.armor.tachidesk.model.database +package ir.armor.tachidesk.model.database.table /* * Copyright (C) Contributors to the Suwayomi project diff --git a/server/src/main/kotlin/ir/armor/tachidesk/model/database/SourceTable.kt b/server/src/main/kotlin/ir/armor/tachidesk/model/database/table/SourceTable.kt similarity index 92% rename from server/src/main/kotlin/ir/armor/tachidesk/model/database/SourceTable.kt rename to server/src/main/kotlin/ir/armor/tachidesk/model/database/table/SourceTable.kt index f3c5a2c..cabcb5b 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/model/database/SourceTable.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/model/database/table/SourceTable.kt @@ -1,4 +1,4 @@ -package ir.armor.tachidesk.model.database +package ir.armor.tachidesk.model.database.table /* * Copyright (C) Contributors to the Suwayomi project diff --git a/server/src/main/kotlin/ir/armor/tachidesk/model/dataclass/MangaDataClass.kt b/server/src/main/kotlin/ir/armor/tachidesk/model/dataclass/MangaDataClass.kt index d11a080..03bb5c8 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/model/dataclass/MangaDataClass.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/model/dataclass/MangaDataClass.kt @@ -7,7 +7,7 @@ package ir.armor.tachidesk.model.dataclass * 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 ir.armor.tachidesk.model.database.MangaStatus +import ir.armor.tachidesk.model.database.table.MangaStatus data class MangaDataClass( val id: Int, diff --git a/server/src/main/kotlin/ir/armor/tachidesk/server/ServerSetup.kt b/server/src/main/kotlin/ir/armor/tachidesk/server/ServerSetup.kt index 31087d1..65053f6 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/server/ServerSetup.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/server/ServerSetup.kt @@ -10,7 +10,7 @@ package ir.armor.tachidesk.server import ch.qos.logback.classic.Level import eu.kanade.tachiyomi.App import ir.armor.tachidesk.Main -import ir.armor.tachidesk.model.database.makeDataBaseTables +import ir.armor.tachidesk.model.database.databaseUp import ir.armor.tachidesk.server.util.systemTray import mu.KotlinLogging import org.kodein.di.DI @@ -91,7 +91,7 @@ fun applicationSetup() { logger.error("Exception while creating initial server.conf:\n", e) } - makeDataBaseTables() + databaseUp() // create system tray if (serverConfig.systemTrayEnabled) {