Skip to content

Commit

Permalink
feat: automatic database backup
Browse files Browse the repository at this point in the history
closes #138
  • Loading branch information
gotson committed Jun 8, 2020
1 parent 4257565 commit bbb9f7c
Show file tree
Hide file tree
Showing 9 changed files with 91 additions and 2 deletions.
1 change: 1 addition & 0 deletions komga/Dockerfile
Expand Up @@ -4,6 +4,7 @@ ARG DEPENDENCY=build/dependency
COPY ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY ${DEPENDENCY}/META-INF /app/META-INF
COPY ${DEPENDENCY}/BOOT-INF/classes /app
ENV KOMGA_DATABASE_BACKUP_PATH="/config/database-backup.zip"
ENV SPRING_DATASOURCE_URL="jdbc:h2:/config/database.h2"
ENV SPRING_ARTEMIS_EMBEDDED_DATA_DIRECTORY="/config/artemis"
ENV LOGGING_FILE_NAME="/config/logs/komga.log"
Expand Down
Expand Up @@ -20,4 +20,9 @@ sealed class Task : Serializable {
data class RefreshBookMetadata(val bookId: Long) : Task() {
override fun uniqueId() = "REFRESH_BOOK_METADATA_$bookId"
}

object BackupDatabase : Task() {
override fun uniqueId(): String = "BACKUP_DATABASE"
override fun toString(): String = "BackupDatabase"
}
}
Expand Up @@ -6,6 +6,7 @@ import org.gotson.komga.domain.persistence.LibraryRepository
import org.gotson.komga.domain.service.BookLifecycle
import org.gotson.komga.domain.service.LibraryScanner
import org.gotson.komga.domain.service.MetadataLifecycle
import org.gotson.komga.infrastructure.h2.DatabaseBackuper
import org.gotson.komga.infrastructure.jms.QUEUE_TASKS
import org.gotson.komga.infrastructure.jms.QUEUE_TASKS_SELECTOR
import org.springframework.jms.annotation.JmsListener
Expand All @@ -21,7 +22,8 @@ class TaskHandler(
private val bookRepository: BookRepository,
private val libraryScanner: LibraryScanner,
private val bookLifecycle: BookLifecycle,
private val metadataLifecycle: MetadataLifecycle
private val metadataLifecycle: MetadataLifecycle,
private val databaseBackuper: DatabaseBackuper
) {

@JmsListener(destination = QUEUE_TASKS, selector = QUEUE_TASKS_SELECTOR)
Expand Down Expand Up @@ -53,6 +55,10 @@ class TaskHandler(
metadataLifecycle.refreshMetadata(it)
} ?: logger.warn { "Cannot execute task $task: Book does not exist" }

is Task.BackupDatabase -> {
databaseBackuper.backupDatabase()
}

}
}.also {
logger.info { "Task $task executed in $it" }
Expand Down
Expand Up @@ -60,6 +60,10 @@ class TaskReceiver(
submitTask(Task.RefreshBookMetadata(book.id))
}

fun databaseBackup() {
submitTask(Task.BackupDatabase)
}

private fun submitTask(task: Task) {
logger.info { "Sending task: $task" }
jmsTemplate.convertAndSend(QUEUE_TASKS, task) {
Expand Down
Expand Up @@ -27,4 +27,13 @@ class KomgaProperties {
@Positive
var validity: Int = 1209600 // 2 weeks
}

var databaseBackup = DatabaseBackup()

class DatabaseBackup {
var enabled: Boolean = true
var path: String = ""
var schedule: String = ""
var startup: Boolean = true
}
}
@@ -0,0 +1,28 @@
package org.gotson.komga.infrastructure.h2

import mu.KotlinLogging
import org.gotson.komga.infrastructure.configuration.KomgaProperties
import org.springframework.jdbc.core.JdbcTemplate
import org.springframework.stereotype.Component
import java.nio.file.Files
import java.nio.file.Paths

private val logger = KotlinLogging.logger {}

@Component
class DatabaseBackuper(
private val jdbcTemplate: JdbcTemplate,
private val komgaProperties: KomgaProperties
) {

fun backupDatabase() {
val path = Paths.get(komgaProperties.databaseBackup.path)

Files.deleteIfExists(path)

val command = "BACKUP TO '$path'"

logger.info { "Executing command: $command" }
jdbcTemplate.execute(command)
}
}
@@ -0,0 +1,29 @@
package org.gotson.komga.interfaces.scheduler

import mu.KotlinLogging
import org.gotson.komga.application.tasks.TaskReceiver
import org.gotson.komga.infrastructure.configuration.KomgaProperties
import org.springframework.boot.context.event.ApplicationReadyEvent
import org.springframework.context.annotation.Profile
import org.springframework.context.event.EventListener
import org.springframework.scheduling.annotation.Scheduled
import org.springframework.stereotype.Controller

private val logger = KotlinLogging.logger {}

@Profile("!test")
@Controller
class PeriodicDatabaseBackupController(
private val taskReceiver: TaskReceiver,
private val komgaProperties: KomgaProperties
) {

@EventListener(classes = [ApplicationReadyEvent::class], condition = "@komgaProperties.databaseBackup.startup")
@Scheduled(cron = "#{@komgaProperties.databaseBackup.schedule ?: '-'}")
fun scanAllLibraries() {
if (komgaProperties.databaseBackup.enabled) {
logger.info { "Periodic database backup" }
taskReceiver.databaseBackup()
}
}
}
6 changes: 5 additions & 1 deletion komga/src/main/resources/application-dev.yml
Expand Up @@ -3,9 +3,13 @@ komga:
remember-me:
key: changeMe!
validity: 2592000 # 1 month
# libraries-scan-cron: "*/5 * * * * ?" #every 5 seconds
# libraries-scan-cron: "*/5 * * * * ?" #every 5 seconds
libraries-scan-cron: "-" #disable
libraries-scan-startup: true
database-backup:
enabled: false
path: ./backup.zip
schedule: "*/15 * * * * ?"
spring:
datasource:
url: jdbc:h2:mem:testdb
Expand Down
3 changes: 3 additions & 0 deletions komga/src/main/resources/application.yml
Expand Up @@ -12,6 +12,9 @@ komga:
libraries-scan-directory-exclusions:
- "#recycle"
- "@eaDir"
database-backup:
path: ~/.komga/database-backup.zip
schedule: "0 0 */6 * * ?"

spring:
# cache:
Expand Down

0 comments on commit bbb9f7c

Please sign in to comment.