Add migrations (#76)

This commit is contained in:
Aria Moradi 2021-05-06 18:44:46 +04:30
parent 5304917e53
commit 63a078cf7d
32 changed files with 352 additions and 63 deletions

View File

@ -20,9 +20,6 @@ repositories {
} }
dependencies { dependencies {
// implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
implementation(kotlin("stdlib-jdk8"))
// Source models and interfaces from Tachiyomi 1.x // Source models and interfaces from Tachiyomi 1.x
// using source class from tachiyomi commit 9493577de27c40ce8b2b6122cc447d025e34c477 to not depend on tachiyomi.sourceapi // using source class from tachiyomi commit 9493577de27c40ce8b2b6122cc447d025e34c477 to not depend on tachiyomi.sourceapi
// implementation("tachiyomi.sourceapi:source-api:1.1") // implementation("tachiyomi.sourceapi:source-api:1.1")
@ -47,7 +44,7 @@ dependencies {
// Reactivex // Reactivex
implementation("io.reactivex:rxjava:1.3.8") 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.google.code.gson:gson:2.8.6")
implementation("com.github.salomonbrys.kotson:kotson:2.5.0") 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-core:$exposedVersion")
implementation("org.jetbrains.exposed:exposed-dao:$exposedVersion") implementation("org.jetbrains.exposed:exposed-dao:$exposedVersion")
implementation("org.jetbrains.exposed:exposed-jdbc:$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") implementation("com.h2database:h2:1.4.200")
// tray icon // tray icon
implementation("com.dorkbox:SystemTray:4.1") implementation("com.dorkbox:SystemTray:4.1")
implementation("com.dorkbox:Utilities:1.9") implementation("com.dorkbox:Utilities:1.9")
implementation("com.google.guava:guava:30.1.1-jre")
// AndroidCompat // AndroidCompat
implementation(project(":AndroidCompat")) implementation(project(":AndroidCompat"))

View File

@ -8,9 +8,9 @@ package ir.armor.tachidesk.impl
* 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 ir.armor.tachidesk.impl.CategoryManga.removeMangaFromCategory import ir.armor.tachidesk.impl.CategoryManga.removeMangaFromCategory
import ir.armor.tachidesk.model.database.CategoryMangaTable import ir.armor.tachidesk.model.database.table.CategoryMangaTable
import ir.armor.tachidesk.model.database.CategoryTable import ir.armor.tachidesk.model.database.table.CategoryTable
import ir.armor.tachidesk.model.database.toDataClass import ir.armor.tachidesk.model.database.table.toDataClass
import ir.armor.tachidesk.model.dataclass.CategoryDataClass import ir.armor.tachidesk.model.dataclass.CategoryDataClass
import org.jetbrains.exposed.sql.SortOrder import org.jetbrains.exposed.sql.SortOrder
import org.jetbrains.exposed.sql.deleteWhere import org.jetbrains.exposed.sql.deleteWhere

View File

@ -7,10 +7,10 @@ package ir.armor.tachidesk.impl
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * 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.table.CategoryMangaTable
import ir.armor.tachidesk.model.database.CategoryTable import ir.armor.tachidesk.model.database.table.CategoryTable
import ir.armor.tachidesk.model.database.MangaTable import ir.armor.tachidesk.model.database.table.MangaTable
import ir.armor.tachidesk.model.database.toDataClass import ir.armor.tachidesk.model.database.table.toDataClass
import ir.armor.tachidesk.model.dataclass.CategoryDataClass import ir.armor.tachidesk.model.dataclass.CategoryDataClass
import ir.armor.tachidesk.model.dataclass.MangaDataClass import ir.armor.tachidesk.model.dataclass.MangaDataClass
import org.jetbrains.exposed.sql.SortOrder import org.jetbrains.exposed.sql.SortOrder

View File

@ -12,9 +12,9 @@ import eu.kanade.tachiyomi.source.model.SManga
import ir.armor.tachidesk.impl.Manga.getManga import ir.armor.tachidesk.impl.Manga.getManga
import ir.armor.tachidesk.impl.util.GetHttpSource.getHttpSource import ir.armor.tachidesk.impl.util.GetHttpSource.getHttpSource
import ir.armor.tachidesk.impl.util.awaitSingle import ir.armor.tachidesk.impl.util.awaitSingle
import ir.armor.tachidesk.model.database.ChapterTable import ir.armor.tachidesk.model.database.table.ChapterTable
import ir.armor.tachidesk.model.database.MangaTable import ir.armor.tachidesk.model.database.table.MangaTable
import ir.armor.tachidesk.model.database.PageTable import ir.armor.tachidesk.model.database.table.PageTable
import ir.armor.tachidesk.model.dataclass.ChapterDataClass import ir.armor.tachidesk.model.dataclass.ChapterDataClass
import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.insert import org.jetbrains.exposed.sql.insert

View File

@ -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.loadExtensionSources
import ir.armor.tachidesk.impl.util.PackageTools.trustedSignatures import ir.armor.tachidesk.impl.util.PackageTools.trustedSignatures
import ir.armor.tachidesk.impl.util.await import ir.armor.tachidesk.impl.util.await
import ir.armor.tachidesk.model.database.ExtensionTable import ir.armor.tachidesk.model.database.table.ExtensionTable
import ir.armor.tachidesk.model.database.SourceTable import ir.armor.tachidesk.model.database.table.SourceTable
import ir.armor.tachidesk.server.ApplicationDirs import ir.armor.tachidesk.server.ApplicationDirs
import mu.KotlinLogging import mu.KotlinLogging
import okhttp3.Request import okhttp3.Request

View File

@ -10,7 +10,7 @@ package ir.armor.tachidesk.impl
import eu.kanade.tachiyomi.extension.api.ExtensionGithubApi import eu.kanade.tachiyomi.extension.api.ExtensionGithubApi
import eu.kanade.tachiyomi.extension.model.Extension import eu.kanade.tachiyomi.extension.model.Extension
import ir.armor.tachidesk.impl.Extension.getExtensionIconUrl 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 ir.armor.tachidesk.model.dataclass.ExtensionDataClass
import mu.KotlinLogging import mu.KotlinLogging
import org.jetbrains.exposed.sql.deleteWhere import org.jetbrains.exposed.sql.deleteWhere

View File

@ -8,9 +8,9 @@ package ir.armor.tachidesk.impl
* 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 ir.armor.tachidesk.impl.Manga.getManga import ir.armor.tachidesk.impl.Manga.getManga
import ir.armor.tachidesk.model.database.CategoryMangaTable import ir.armor.tachidesk.model.database.table.CategoryMangaTable
import ir.armor.tachidesk.model.database.MangaTable import ir.armor.tachidesk.model.database.table.MangaTable
import ir.armor.tachidesk.model.database.toDataClass import ir.armor.tachidesk.model.database.table.toDataClass
import ir.armor.tachidesk.model.dataclass.MangaDataClass import ir.armor.tachidesk.model.dataclass.MangaDataClass
import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.deleteWhere import org.jetbrains.exposed.sql.deleteWhere

View File

@ -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.GetHttpSource.getHttpSource
import ir.armor.tachidesk.impl.util.await import ir.armor.tachidesk.impl.util.await
import ir.armor.tachidesk.impl.util.awaitSingle import ir.armor.tachidesk.impl.util.awaitSingle
import ir.armor.tachidesk.model.database.MangaStatus import ir.armor.tachidesk.model.database.table.MangaStatus
import ir.armor.tachidesk.model.database.MangaTable import ir.armor.tachidesk.model.database.table.MangaTable
import ir.armor.tachidesk.model.dataclass.MangaDataClass import ir.armor.tachidesk.model.dataclass.MangaDataClass
import ir.armor.tachidesk.server.ApplicationDirs import ir.armor.tachidesk.server.ApplicationDirs
import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.select

View File

@ -10,8 +10,8 @@ package ir.armor.tachidesk.impl
import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.MangasPage
import ir.armor.tachidesk.impl.util.GetHttpSource.getHttpSource import ir.armor.tachidesk.impl.util.GetHttpSource.getHttpSource
import ir.armor.tachidesk.impl.util.awaitSingle import ir.armor.tachidesk.impl.util.awaitSingle
import ir.armor.tachidesk.model.database.MangaStatus import ir.armor.tachidesk.model.database.table.MangaStatus
import ir.armor.tachidesk.model.database.MangaTable import ir.armor.tachidesk.model.database.table.MangaTable
import ir.armor.tachidesk.model.dataclass.MangaDataClass import ir.armor.tachidesk.model.dataclass.MangaDataClass
import ir.armor.tachidesk.model.dataclass.PagedMangaListDataClass import ir.armor.tachidesk.model.dataclass.PagedMangaListDataClass
import org.jetbrains.exposed.sql.insertAndGetId import org.jetbrains.exposed.sql.insertAndGetId

View File

@ -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.CachedImageResponse.getCachedImageResponse
import ir.armor.tachidesk.impl.util.GetHttpSource.getHttpSource import ir.armor.tachidesk.impl.util.GetHttpSource.getHttpSource
import ir.armor.tachidesk.impl.util.awaitSingle import ir.armor.tachidesk.impl.util.awaitSingle
import ir.armor.tachidesk.model.database.ChapterTable import ir.armor.tachidesk.model.database.table.ChapterTable
import ir.armor.tachidesk.model.database.MangaTable import ir.armor.tachidesk.model.database.table.MangaTable
import ir.armor.tachidesk.model.database.PageTable import ir.armor.tachidesk.model.database.table.PageTable
import ir.armor.tachidesk.model.database.SourceTable import ir.armor.tachidesk.model.database.table.SourceTable
import ir.armor.tachidesk.server.ApplicationDirs import ir.armor.tachidesk.server.ApplicationDirs
import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.select

View File

@ -9,8 +9,8 @@ package ir.armor.tachidesk.impl
import ir.armor.tachidesk.impl.Extension.getExtensionIconUrl import ir.armor.tachidesk.impl.Extension.getExtensionIconUrl
import ir.armor.tachidesk.impl.util.GetHttpSource.getHttpSource import ir.armor.tachidesk.impl.util.GetHttpSource.getHttpSource
import ir.armor.tachidesk.model.database.ExtensionTable import ir.armor.tachidesk.model.database.table.ExtensionTable
import ir.armor.tachidesk.model.database.SourceTable import ir.armor.tachidesk.model.database.table.SourceTable
import ir.armor.tachidesk.model.dataclass.SourceDataClass import ir.armor.tachidesk.model.dataclass.SourceDataClass
import mu.KotlinLogging import mu.KotlinLogging
import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.select

View File

@ -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.Manga
import ir.armor.tachidesk.impl.backup.models.MangaImpl import ir.armor.tachidesk.impl.backup.models.MangaImpl
import ir.armor.tachidesk.impl.util.GetHttpSource.getHttpSource import ir.armor.tachidesk.impl.util.GetHttpSource.getHttpSource
import ir.armor.tachidesk.model.database.ChapterTable import ir.armor.tachidesk.model.database.table.ChapterTable
import ir.armor.tachidesk.model.database.MangaTable import ir.armor.tachidesk.model.database.table.MangaTable
import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction

View File

@ -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.backup.models.TrackImpl
import ir.armor.tachidesk.impl.util.GetHttpSource.getHttpSource import ir.armor.tachidesk.impl.util.GetHttpSource.getHttpSource
import ir.armor.tachidesk.impl.util.awaitSingle 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 mu.KotlinLogging
import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.insert import org.jetbrains.exposed.sql.insert

View File

@ -9,7 +9,7 @@ package ir.armor.tachidesk.impl.backup.legacy
import com.google.gson.JsonObject import com.google.gson.JsonObject
import ir.armor.tachidesk.impl.backup.legacy.models.Backup 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.select
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction

View File

@ -1,6 +1,6 @@
package ir.armor.tachidesk.impl.backup.models 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 import org.jetbrains.exposed.sql.ResultRow
class ChapterImpl : Chapter { class ChapterImpl : Chapter {

View File

@ -1,6 +1,6 @@
package ir.armor.tachidesk.impl.backup.models 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 import org.jetbrains.exposed.sql.ResultRow
open class MangaImpl : Manga { open class MangaImpl : Manga {

View File

@ -11,8 +11,8 @@ import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceFactory import eu.kanade.tachiyomi.source.SourceFactory
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import ir.armor.tachidesk.impl.util.PackageTools.loadExtensionSources import ir.armor.tachidesk.impl.util.PackageTools.loadExtensionSources
import ir.armor.tachidesk.model.database.ExtensionTable import ir.armor.tachidesk.model.database.table.ExtensionTable
import ir.armor.tachidesk.model.database.SourceTable import ir.armor.tachidesk.model.database.table.SourceTable
import ir.armor.tachidesk.server.ApplicationDirs import ir.armor.tachidesk.server.ApplicationDirs
import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction

View File

@ -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 * 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/. */ * 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 ir.armor.tachidesk.server.ApplicationDirs
import org.jetbrains.exposed.sql.Database 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.DI
import org.kodein.di.conf.global import org.kodein.di.conf.global
import org.kodein.di.instance import org.kodein.di.instance
@ -22,20 +22,11 @@ object DBMangaer {
} }
} }
fun makeDataBaseTables() { fun databaseUp() {
// must mention db object so the lazy block executes // must mention db object so the lazy block executes
val db = DBMangaer.db val db = DBMangaer.db
db.useNestedTransactions = true db.useNestedTransactions = true
transaction { val migrations = loadMigrationsFrom("ir.armor.tachidesk.model.database.migration")
SchemaUtils.createMissingTablesAndColumns( runMigrations(migrations)
ExtensionTable,
SourceTable,
MangaTable,
ChapterTable,
PageTable,
CategoryTable,
CategoryMangaTable,
)
}
} }

View File

@ -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<Long>() {
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,
)
}
}
}

View File

@ -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.

View File

@ -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()
}

View File

@ -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<Int>() {
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<Int>) : IntEntity(id) {
companion object : IntEntityClass<MigrationEntity>(MigrationsTable)
var version by MigrationsTable.id
var name by MigrationsTable.name
var executedAt by MigrationsTable.executedAt
}

View File

@ -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<Migration>, 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<Migration> {
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<Migration>) {
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
}

View File

@ -1,4 +1,4 @@
package ir.armor.tachidesk.model.database package ir.armor.tachidesk.model.database.table
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project

View File

@ -1,4 +1,4 @@
package ir.armor.tachidesk.model.database package ir.armor.tachidesk.model.database.table
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project

View File

@ -1,4 +1,4 @@
package ir.armor.tachidesk.model.database package ir.armor.tachidesk.model.database.table
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project

View File

@ -1,4 +1,4 @@
package ir.armor.tachidesk.model.database package ir.armor.tachidesk.model.database.table
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project

View File

@ -1,4 +1,4 @@
package ir.armor.tachidesk.model.database package ir.armor.tachidesk.model.database.table
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project
@ -37,7 +37,7 @@ object MangaTable : IntIdTable() {
fun MangaTable.toDataClass(mangaEntry: ResultRow) = fun MangaTable.toDataClass(mangaEntry: ResultRow) =
MangaDataClass( MangaDataClass(
mangaEntry[MangaTable.id].value, mangaEntry[MangaTable.id].value,
mangaEntry[sourceReference].toString(), mangaEntry[MangaTable.sourceReference].toString(),
mangaEntry[MangaTable.url], mangaEntry[MangaTable.url],
mangaEntry[MangaTable.title], mangaEntry[MangaTable.title],

View File

@ -1,4 +1,4 @@
package ir.armor.tachidesk.model.database package ir.armor.tachidesk.model.database.table
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project

View File

@ -1,4 +1,4 @@
package ir.armor.tachidesk.model.database package ir.armor.tachidesk.model.database.table
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project

View File

@ -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 * 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/. */ * 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( data class MangaDataClass(
val id: Int, val id: Int,

View File

@ -10,7 +10,7 @@ package ir.armor.tachidesk.server
import ch.qos.logback.classic.Level import ch.qos.logback.classic.Level
import eu.kanade.tachiyomi.App import eu.kanade.tachiyomi.App
import ir.armor.tachidesk.Main 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 ir.armor.tachidesk.server.util.systemTray
import mu.KotlinLogging import mu.KotlinLogging
import org.kodein.di.DI import org.kodein.di.DI
@ -91,7 +91,7 @@ fun applicationSetup() {
logger.error("Exception while creating initial server.conf:\n", e) logger.error("Exception while creating initial server.conf:\n", e)
} }
makeDataBaseTables() databaseUp()
// create system tray // create system tray
if (serverConfig.systemTrayEnabled) { if (serverConfig.systemTrayEnabled) {